2014-11-02 27 views
16

Tôi có templated gray_code lớp đó là có nghĩa là để lưu trữ một số nguyên unsigned có bit cơ bản được lưu trữ trong thứ tự mã màu xám. Ở đây là:Khởi tạo giá trị: khởi tạo mặc định hoặc khởi tạo không?

template<typename UnsignedInt> 
struct gray_code 
{ 
    static_assert(std::is_unsigned<UnsignedInt>::value, 
        "gray code only supports built-in unsigned integers"); 

    // Variable containing the gray code 
    UnsignedInt value; 

    // Default constructor 
    constexpr gray_code() 
     = default; 

    // Construction from UnsignedInt 
    constexpr explicit gray_code(UnsignedInt value): 
     value((value >> 1)^value) 
    {} 

    // Other methods... 
}; 

Trong một số thuật toán chung, tôi đã viết một cái gì đó như thế này:

template<typename UnsignedInt> 
void foo(/* ... */) 
{ 
    gray_code<UnsignedInt> bar{}; 
    // Other stuff... 
} 

Trong đoạn mã này, tôi mong đợi bar là zero-intialized và do đó bar.value là zero- được khởi tạo. Tuy nhiên, sau khi đấu tranh với các lỗi không mong muốn, có vẻ như bar.value được khởi tạo với rác (chính xác là 4606858) thay vì 0u. Đó là làm tôi ngạc nhiên, vì vậy tôi đã đi đến cppreference.com để xem những gì các dòng trên được chính xác phải làm gì ...


Từ những gì tôi có thể đọc, hình thức T object{}; tương ứng với value initialization. Tôi thấy báo giá này thú vị:

Trong tất cả các trường hợp, nếu cặp dấu ngoặc nhọn {} được sử dụng và T là một loại tổng hợp, tổng hợp-khởi tạo được thực hiện thay vì khởi tạo giá trị.

Tuy nhiên, gray_code có một hàm tạo do người dùng cung cấp. Do đó nó không phải là một tổng hợp do đó aggregate initialization không được thực hiện. gray_code không có hàm tạo nào tham gia std::initializer_list do đó list initialization cũng không được thực hiện. Các giá trị khởi tạo của gray_code thì nên làm theo các thông thường C++ 14 quy tắc của giá trị khởi tạo:

1) Nếu T là một loại lớp không có constructor mặc định hoặc với một constructor mặc định người dùng cung cấp hoặc với một xóa constructor mặc định, đối tượng được khởi tạo mặc định.

2) Nếu T là loại lớp không có trình tạo mặc định do người dùng cung cấp hoặc bị xóa (có nghĩa là, nó có thể là một lớp với hàm tạo mặc định) hoặc đối tượng không được khởi tạo và sau đó nó được khởi tạo mặc định nếu nó có một hàm tạo mặc định không tầm thường.

3) Nếu T là một loại mảng, mỗi phần tử của mảng được khởi tạo giá trị.

4) Nếu không, đối tượng không được khởi tạo.

Nếu tôi đọc một cách chính xác, gray_code có một constructor mặc định mặc định một cách rõ ràng (không dùng cung cấp), do đó 1) không áp dụng. Nó có một hàm tạo mặc định mặc định, vì vậy 2) áp dụng: gray_codezero-initialized. Mặc định constructor mặc định dường như đáp ứng tất cả các yêu cầu của một constructor mặc định tầm thường, do đó, khởi tạo mặc định không nên xảy ra. Chúng ta hãy có một cái nhìn đó như thế nào gray_code là zero-khởi tạo:

  • Nếu T là một kiểu vô hướng, giá trị ban đầu của đối tượng là không liên tục không thể thiếu ngầm chuyển đổi sang T.

  • Nếu T là một loại lớp không phải công đoàn, tất cả các lớp cơ sở và thành viên dữ liệu không tĩnh đều được khởi tạo bằng 0 và tất cả các phần đệm được khởi tạo thành 0 bit. Các nhà thầu, nếu có, được bỏ qua.

  • Nếu T là kiểu kết hợp, thành viên dữ liệu không được đặt tên đầu tiên không được khởi tạo bằng 0 và tất cả các phần đệm được khởi tạo thành 0 bit.

  • Nếu T là kiểu mảng, mỗi phần tử là zero-khởi

  • Nếu T là kiểu tham chiếu, không có gì được thực hiện.

gray_code là một loại lớp phi công đoàn. Vì vậy, tất cả các thành viên dữ liệu không tĩnh của nó nên được khởi tạo có nghĩa là value không được khởi tạo. value thỏa mãn std::is_unsigned và do đó là một loại vô hướng, có nghĩa là nó phải được khởi tạo với "hằng số không đổi không được chuyển đổi hoàn toàn thành T". Vì vậy, nếu tôi đọc chính xác tất cả những điều đó, trong hàm foo ở trên, bar.value phải luôn được khởi tạo với 0 và nó không bao giờ nên được khởi tạo với rác, tôi có đúng không? Không phải lúc nào cũng được khởi tạo?

Lưu ý: trình biên dịch tôi biên dịch mã của tôi là MinGW_w4 GCC 4.9.1 với (chủ đề POSIX và ngoại lệ lùn) trong trường hợp giúp. Trong khi đôi khi tôi nhận được rác trên máy tính của tôi, tôi không bao giờ quản lý để có được bất cứ điều gì khác hơn không với các trình biên dịch trực tuyến.


Cập nhật: Dường là một GCC lỗi rằng lỗi là của tôi và không phải của trình biên dịch của tôi. Trên thực tế, khi viết câu hỏi này, tôi cho rằng vì lợi ích của sự đơn giản mà

class foo { 
    foo() = default; 
}; 

class foo { 
    foo(); 
}; 

foo::foo() = default; 

là tương đương. Họ không phải. Dưới đây là trích dẫn từ C++ 14 tiêu chuẩn, phần [dcl.fct.def.default]:

Một chức năng là dùng cung cấp nếu nó là do người dùng khai báo và không rõ ràng mặc định hoặc xóa trên tờ khai đầu tiên.

Nói cách khác, khi tôi có giá trị rác, constructor mặc định defaulted tôi đã thực sự dùng cung cấp vì nó đã không được rõ ràng efaulted trên tuyên bố đầu tiên của mình. Vì vậy, những gì đã xảy ra không phải là khởi tạo zero nhưng khởi tạo mặc định. Cảm ơn @Columbo lần nữa vì đã chỉ ra vấn đề thực sự.

+4

[dcl.fct.def.default]/5 "một chức năng được người dùng cung cấp nếu nó được người dùng khai báo và ** không được mặc định rõ ràng ** hoặc bị xóa trên khai báo đầu tiên". Nhà xây dựng của bạn không phải do người dùng cung cấp. – user657267

+0

Bạn có thể đã viết 'UnsignedInt value {0}' thay vì đọc các trang và trang ... Tôi biết bạn biết. ;-) – davidhigh

+1

lưu ý ... khởi tạo giá trị STILL không hoạt động trên trình biên dịch của Microsoft dưới dạng VS2013 – Mgetz

Trả lời

9

Vì vậy, nếu tôi đọc một cách chính xác tất cả điều đó, trong hàm foo trên, bar.value phải luôn luôn được khởi tạo với 0 và nó sẽ không bao giờ được khởi tạo với rác, tôi phải không?

Có. Đối tượng của bạn được khởi tạo trực tiếp trong danh sách. C++ 14 của * [dcl.init.list]/3 quy định rằng

List-khởi của một đối tượng hoặc tài liệu tham khảo của loại T được định nghĩa như sau:

  • [... không áp đạn điểm…]

  • Nếu không, nếu T là tổng hợp, việc khởi tạo tổng hợp được thực hiện (8.5.1).

  • Nếu không, nếu danh sách trình khởi tạo không có phần tử và T là loại lớp với hàm tạo mặc định, đối tượng sẽ được khởi tạo giá trị.

  • [...]

lớp học của bạn không phải là một tổng hợp vì nó có nhà xây dựng người dùng cung cấp, nhưng nó có một constructor mặc định. [Dcl.init]/7:

Để giá trị khởi tạo một đối tượng kiểu T có nghĩa là:

  • nếu T là một (có thể cv-đủ điều kiện) kiểu lớp (khoản 9) không có hàm tạo mặc định (12.1) hoặc một hàm tạo mặc định là do người dùng cung cấp hoặc xóa, khi đó đối tượng được khởi tạo mặc định;

  • nếu T là một (có thể cv-đủ điều kiện) lớp loại mà không một người dùng cung cấp hoặc xóa constructor mặc định, sau đó đối tượng là zero-khởi tạo và các khó khăn về ngữ nghĩa cho mặc định-khởi được kiểm tra, và nếu T có một hàm tạo mặc định không tầm thường , đối tượng được khởi tạo mặc định;

[dcl.fct.def.default]/4:

Một hàm thành viên đặc biệt là dùng cung cấp nếu nó là sử dụng tuyên bố và không rõ ràng mặc định [ ...] trên tờ khai đầu tiên.

Vì vậy, hàm tạo của bạn không được người dùng cung cấp, do đó đối tượng không được khởi tạo.(Các nhà xây dựng không được gọi từ tầm thường của nó)

Và cuối cùng, trong trường hợp này là không rõ ràng, để zero-khởi một đối tượng hoặc tài liệu tham khảo của loại T có nghĩa là:

  • nếu T là một loại vô hướng (3.9), đối tượng được khởi tạo thành giá trị thu được bằng cách chuyển đổi số nguyên 0 (số không) thành T;

  • nếu T là loại không phải là công đoàn, mỗi thành viên dữ liệu không tĩnh và mỗi lớp con cấp cơ sở không được khởi tạo và đệm được khởi tạo thành 0 bit;

  • [...]


Do đó một trong hai

  • trình biên dịch của bạn được nghe trộm

  • ... hoặc mã của bạn gây ra hành vi undefined tại một số điểm khác.


* Câu trả lời vẫn có là trong C++ 11, mặc dù các phần trích dẫn không phải là tương đương.

+0

Tôi đã xem xét hội đồng được tạo ra. Hàm khởi tạo mặc định có một lệnh có ý nghĩa, 'mov% ecx, -0x4 (% ebp)'. Nếu tôi hiểu rõ, nó đặt địa chỉ 'bar' nhưng không bao giờ đặt' giá trị'. – Morwenn

+0

@Morwenn Tất nhiên, hàm tạo không đặt 'giá trị'. Mặc định tạo ra constructor mặc định khởi tạo-khởi tạo các đối tượng thành viên có nghĩa là không khởi tạo được thực hiện cho vô hướng. – Columbo

+0

Bạn nói đúng, có vẻ như tôi đã nhầm lẫn giữa nhà xây dựng và phần intialization. Điều đó nói rằng, constructor mặc định mặc định được gọi trong trường hợp của tôi. Nhưng không có gì khác gần trang web cuộc gọi này dường như khởi tạo 'giá trị'. – Morwenn

Các vấn đề liên quan