24

Trình tạo bản sao rõ ràng không cho phép một cái gì đó như Foo foo = bar; và thực thi việc sử dụng bản sao là Foo foo(bar);. Ngoài ra, các nhà xây dựng bản sao rõ ràng cũng không cho phép các đối tượng trả về theo giá trị từ một hàm. Tuy nhiên, tôi đã cố gắng thay thế khởi copy với niềng răng, như vậyTrình tạo bản sao rõ ràng và khởi tạo đồng bộ

struct Foo 
{ 
    Foo() = default; 
    explicit Foo(const Foo&) = default; 
}; 

int main() 
{ 
    Foo bar; 
    Foo foo{bar}; // error here 
} 

và tôi nhận được lỗi (g ++ 5,2)

error: no matching function for call to 'Foo::Foo(Foo&)'

hoặc (kêu vang ++)

error: excess elements in struct initializer

Loại bỏ các explicit làm cho mã compilable theo g + +, clang + + vẫn không thành công với cùng một lỗi (nhờ @Steephen). Những gì đang xảy ra ở đây? Khởi tạo thống nhất có được coi là một hàm khởi tạo danh sách khởi tạo (nó vượt trội hơn tất cả các hàm khác) không? Nhưng nếu đó là trường hợp, tại sao chương trình biên dịch khi các nhà xây dựng bản sao là không rõ ràng?

+3

clang hiển thị lỗi ngay cả khi không có từ khóa 'clear' http://coliru.stacked-crooked.com/a/1cca94237ee00ea3 – Steephen

+0

@Stephen thực sự, tôi không thấy điều đó. Vì vậy, nó là một câu hỏi kiểu ngôn ngữ-luật sư sôi nổi :) – vsoftco

+1

Tôi sẽ nói rằng trình biên dịch chọn để tổng hợp khởi tạo 'foo'. – Jarod42

Trả lời

23

Bạn đã gặp phải trường hợp được giải quyết theo độ phân giải Core issue 1467 ngay sau khi C++ 14 được hoàn tất.

Hãy lưu ý rằng lớp học foo là tổng hợp. Mã của bạn đang thực hiện khởi tạo danh sách trực tiếp cho foo. Các quy tắc cho việc khởi tạo danh sách nằm trong [8.5.4p3].

Trong C++ 14 (trích dẫn từ N4140, dự thảo làm việc gần nhất với tiêu chuẩn công bố), đoạn bắt đầu trên với:

List-initialization of an object or reference of type T is defined as follows:

  • If T is an aggregate, aggregate initialization is performed (8.5.1).

[...]

Vì vậy, nếu lớp học của bạn là một tổng hợp, trình biên dịch cố gắng khởi tạo tổng hợp, không thành công.

Điều này đã được công nhận là sự cố và được khắc phục trong bản nháp đang hoạt động. Trích dẫn từ phiên bản hiện tại, N4527, đoạn nêu trên hiện nay bắt đầu với:

List-initialization of an object or reference of type T is defined as follows:

  • If T is a class type and the initializer list has a single element of type cvU , where U is T or a class derived from T , the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).
  • Otherwise, if T is a character array and the initializer list has a single element that is an appropriately typed string literal (8.5.2), initialization is performed as described in that section.
  • Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).

[...]

dụ của bạn bây giờ nằm ​​trong trường hợp được mô tả bởi các điểm viên đạn đầu tiên, và footrực tiếp-list-khởi sử dụng hàm tạo bản sao mặc định (bất kể đó là explicit, vì nó khởi tạo trực tiếp).

Đó là ... nếu trình biên dịch thực hiện độ phân giải trong báo cáo lỗi.

  • GCC 5.2.0 (và 6.0.0 trunk) có vẻ như vậy, nhưng dường như có lỗi liên quan đến số explicit.
  • Clang 3.6.0 không, nhưng 3.8.0 thân cây, và làm nó một cách chính xác (explicit không quan trọng).
  • MSVC 14, nhưng IntelliSense trong IDE không (squiggles dưới bar - trông giống như trình biên dịch EDG được sử dụng bởi IntelliSense không được cập nhật).

Cập nhật: Kể từ khi câu trả lời này được viết ra, dự thảo làm việc đã được sửa đổi hơn nữa trong một vài cách có liên quan đến ví dụ trong câu hỏi và giải thích ở trên:

  • CWG 2137 chỉ ra rằng viên đạn đầu tiên trong đoạn trích dẫn ở trên đã đi quá xa một chút bằng cách áp dụng ngoại lệ đó cho tất cả các loại lớp (ghi chú vấn đề chứa một ví dụ liên quan). Sự khởi đầu của viên đạn bây giờ đọc:
    • If T is an aggregate class and [...]
  • Độ phân giải của CWG 1518 chứa trong giấy P0398R0 chỉ ra rằng lớp tuyên bố một explicit constructor (thậm chí defaulted) không còn uẩn.

Điều này không thay đổi thực tế là, sau khi tất cả thay đổi được thực hiện, ví dụ trong câu hỏi được dự định làm việc, có hoặc không có explicit; nó chỉ là giá trị biết rằng các cơ chế cơ bản mà làm cho nó hoạt động đã thay đổi một chút.

Lưu ý rằng tất cả những thay đổi này là độ phân giải của các báo cáo lỗi, vì vậy chúng phải được áp dụng khi các trình biên dịch cũng ở chế độ C++ 14 và C++ 11.

+0

Thú vị. Mặc dù một điều mà tôi vẫn không hiểu là lý do tại sao mã biên dịch mà không có compaints (MSVC + GCC) nếu chỉ cần thay đổi constructor sao chép để 'rõ ràng Foo (const Foo &) {}' thay vì '= mặc định;'? – Christophe

+4

@Christophe Vì 'Foo' không còn là tổng hợp trong trường hợp đó - nó có một hàm tạo do người dùng cung cấp. – bogdan

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