2015-04-16 22 views
24

Để bắt đầu, tôi có một cấu trúc với một giá trị với giá trị mặc địnhconst T {}; hoạt động, const T; thất bại khi T là một tổ chức phi POD,

struct S { 
    int a = 1; 
}; 

loại này có thể được mặc định được xây dựng khi nó là không const/phi constexpr bởi cả gcc và clang. Trong cả hai, std::is_pod<S>::valuefalse. Các hành vi kỳ lạ như sau:

S s1; // works under both 
const S s2{}; // works under both 
const S s3; // only works in gcc, clang wants a user-provided constructor 

Không ai trong số những nỗ lực sau làm cho một sự khác biệt để Clang:

struct S { 
    int a = 1; 
    constexpr S() = default; // defaulted ctor 
    virtual void f() { } // virtual function, not an aggregate 
    private: 
    int b = 2; // private member, really not an aggregate 
}; 

Điều duy nhất tôi có thể làm điều đó làm cho công việc này là thêm constexpr S() { } một cách rõ ràng. Có vẻ như thực sự sai với tôi rằng const S s; không thành công khi const S s{}; đặc biệt là khi loại không phải là tổng hợp.

Tiêu chuẩn khiến tôi nghĩ rằng Clang là đúng
N4296: 8,5/7

Nếu một chương trình đòi hỏi phải khởi tạo mặc định của một đối tượng của một loại T const-đủ điều kiện, T sẽ là kiểu lớp với một constructor mặc định dùng cung cấp

Vậy tại sao gcc cho phép điều này, và là S{}; không mặc định khởi tạo, ngay cả khi loại không phải là một POD hay một tổng?

+2

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#253; cũng '{}' là khởi tạo giá trị cho các số không tổng hợp. –

+0

Có thể trùng lặp của [Tại sao C++ yêu cầu một hàm tạo mặc định do người dùng cung cấp để mặc định xây dựng một đối tượng const?] (Https://stackoverflow.com/questions/7411515/why-does-c-require-a-user-provided -default-constructor-to-default-construct-a) –

+0

@ David Tôi nghĩ có thêm một chút ở đây với '= default' –

Trả lời

16
const S s3; 

Được bao phủ bởi [dcl.init]/12:

Nếu không khởi tạo được quy định cho một đối tượng, đối tượng là mặc định-khởi tạo.

Do đó, theo yêu cầu của báo giá của bạn, người xây dựng mặc định do người dùng cung cấp phải có mặt. Thêm một cái như vậy

struct S { 
    int a = 1; 
    constexpr S(){} 
}; 

rồi makes the declaration compile fine.

[..] đặc biệt khi loại không phải là tổng hợp.

Slà một tổng hợp trong trường hợp của bạn, và lý do tại sao const S s{} là hợp lệ. Tổng hợp khởi tạo được áp dụng cho const S s{} và mọi thứ đều ổn.
Nếu S không phải là một tổng hợp,

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 là sau:

  • Nếu T là một tổng hợp, tổng hợp khởi tạo đượ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 có hàm tạo mặc định, đối tượng được khởi tạo giá trị.

Bây giờ xem xét các định nghĩa về giá trị khởi tạo:

Để 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 -trường hợp loại) (Điều 9) không có phương thức khởi tạo mặc định là (12.1)) hoặc một hàm tạo mặc định 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) kiểu lớp mà không có một người dùng cung cấp hoặc xóa mặc định constructor, 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ó hàm tạo mặc định không tầm thường, đối tượng được khởi tạo mặc định;

Các ctor mặc định là thực sự không tầm thường từ một thành viên đã có một khởi tạo ([class.ctor] /4.9), nhưng đó là không thích hợp vì những hạn chế được kiểm tra eitherway. Do đó mặc định-khởi tạo nó là, và dòng

const S s{}; 

là chỉ có giá trị như

const S t; 

Vậy tại sao gcc cho phép điều này

Vâng (hoặc không hợp lệ!):

  1. Spe aking về các tiêu chuẩn hiện hành, GCC không tuân thủ; Xem ở trên.

  2. Có một vấn đề CWG hoạt động, #253, tạo ra mười lăm năm trước, bao gồm một kịch bản tương tự. Lưu ý cuối cùng về thông báo này từ cuộc họp năm 2011 cho biết

    Nếu hàm tạo mặc định ngầm khởi tạo tất cả các subobject, không cần khởi tạo.

    Đó là trường hợp với các nhà xây dựng mặc định ngầm cho S, và điều này sẽ làm cho tất cả các dòng của bạn hợp lệ.

  3. nhà phát triển GCC (ví dụ: here) ngụ ý rằng vì ủy ban đã đồng ý về giải pháp trên, hành vi hiện tại của GCC là khả thi và không được điều chỉnh. Vì vậy, người ta cũng có thể lập luận rằng GCC là đúng và tiêu chuẩn bị hỏng.

+0

Xem https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57820 và https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60284. GCC đang thực hiện độ phân giải dự định cho CWG 253. –

+0

@ T.C. Cố ý? Nhưng thậm chí sau đó, nó không phải là tiêu chuẩn tuân thủ ... không nên họ "sửa chữa" lỗi cho đến khi vấn đề này được đóng lại? – Columbo

+0

Thời điểm đầu tư thay đổi trình biên dịch để phá vỡ mã người dùng là gì khi bạn biết rằng tiêu chuẩn sẽ được thay đổi để cho phép mã như vậy? –

5

Vì vậy, có vẻ như gcc dựa trên số này là DR 253 mặc dù điều này chưa được giải quyết. Chúng ta có thể thấy điều này từ gcc bug report sau đây có nội dung:

Đây là thiết kế, vì như DR 253 cho thấy, tiêu chuẩn quy định là thiếu sót.

gcc change that brought this into effect nói:

Lõi 234 - cho phép đối tượng const không có initializer hoặc constructor mặc định người dùng cung cấp nếu các nhà xây dựng defaulted khởi tạo tất cả các subobjects.

Vì vậy, về mặt kỹ thuật clang là đúng và gcc là không tuân thủ QTI nhưng nó có vẻ như họ tin DR 253 sẽ được giải quyết có lợi cho họ. Điều này có ý nghĩa hoàn toàn nếu mối quan tâm chính là giá trị ban đầu không xác định theo như tôi có thể nói. Sự thay đổi này được ghi chép lại trong gcc 4.6 release notes:

Trong 4.6.0 và 4.6.1 G ++ không còn cho phép các đối tượng const trình độ loại được mặc định khởi trừ loại có một constructor mặc định do người dùng khai báo. Trong 4.6.2 G ++ thực hiện độ phân giải được đề xuất của DR 253, vì vậy khởi tạo mặc định được cho phép nếu nó khởi tạo tất cả các đối tượng phụ . Mã không thể biên dịch có thể được sửa bằng cách cung cấp trình khởi tạo ví dụ:

struct A { A(); }; 
struct B : A { int i; }; 
const B b = B(); 
Các vấn đề liên quan