2015-10-10 14 views
7
struct A { A(int);}; 
struct B { explicit B(A); B(const B&);}; 
B b({0}); 

gcc 5.1.0 cung cấp cho các lỗiđộ phân giải quá tải được kết quả khác nhau giữa gcc và kêu vang

/dev/fd/63:3:8: error: call of overloaded 'B(<brace-enclosed initializer list>)' 
is ambiguous 
/dev/fd/63:3:8: note: candidates are: 
/dev/fd/63:2:27: note: B::B(const B&) 
/dev/fd/63:2:21: note: B::B(A) 

trong khi 3.6.0 vang thành công.

Điều nào là đúng? Tại sao?

Đối với gcc 5.1.0: http://melpon.org/wandbox/permlink/pVe9eyXgu26NEX6X

Đối kêu vang 3.6.0: http://melpon.org/wandbox/permlink/WOi1md2dc519SPW0

Đây có thể là tương tự như Direct list initialization compiles successfully, but normal direct initialization fails, why? mà gcc và kêu vang có được kết quả tương tự.

Nhưng đây là một câu hỏi khác. B(A) rõ ràng ở đây. gcc và clang nhận được kết quả khác nhau.

+0

Bản sao của http://stackoverflow.com/q/32469979/3647361? – Columbo

+0

@Columbo 'B (A)' là rõ ràng ở đây và http://stackoverflow.com/questions/32469979/direct-list-initialization-compiles-successfully-but-normal-direct-initializati cả gcc và clang có cùng kết quả nhưng đây là khác nhau. – stackcpp

+0

Tôi đoán là do các tiêu chuẩn mặc định khác nhau/hỗ trợ chuẩn. –

Trả lời

3

Sự khác biệt có thể được giảm xuống còn

struct A { explicit A(int); }; 
struct B { B(int); }; 
void f(A); 
void f(B); 

int main() { 
    f({ 1 }); 
} 

On GCC này không thành công, phù hợp với các tiêu chuẩn (mà nói rằng đối với danh sách khởi tạo, nhà xây dựng rõ ràng được coi là - vì vậy họ có thể mang lại một sự mơ hồ - nhưng họ chỉ không được phép chọn). Clang chấp nhận nó và gọi hàm thứ hai.

Trong trường hợp của bạn, những gì @Columbo nói trong câu trả lời của mình là Direct list initialization compiles successfully, but normal direct initialization fails, why? sẽ được áp dụng. Với sự khác biệt trong trường hợp của bạn, B(const B&); không còn được Clang chấp nhận vì chuyển đổi {0} -> B sẽ phải đối mặt với hai khả năng: hàm tạo rõ ràng hoặc sử dụng hàm tạo bản sao đệ quy lần thứ hai. Tùy chọn đầu tiên, như được giải thích ở trên, sẽ không được xem xét bởi clang và lần này giải thích bởi @Columbo được áp dụng và không thể sử dụng hàm tạo bản sao lần thứ hai vì điều đó cần chuyển đổi do người dùng xác định vì chúng tôi có một phần tử duy nhất (ở đây, 0). Vì vậy, trong bản tóm tắt, chỉ có các nhà xây dựng đầu tiên thành công và được thực hiện.


Vì tôi hiểu vấn đề là về các quy tắc giải quyết quá tải lạ và một số có thể không tuân theo, đây là giải thích trực quan hơn. Các quy tắc đang hoạt động là, để

  • b({0}) nghĩa goto http://eel.is/c++draft/dcl.init#17 và từ đó đến http://eel.is/c++draft/over.match.ctor đó là lần đầu tiên HOẶC bối cảnh của chúng tôi. Hai nhà thầu được liệt kê là B(A);B(const B&) với đối số {0}.

    • Đối với B(A), nó hoạt động với một chuyển đổi do người dùng xác định.

    • Đối B(const B&), chúng ta cần phải khởi tạo một const B& đó đưa chúng ta đến http://eel.is/c++draft/over.ics.list#8 sau đó đến http://eel.is/c++draft/over.ics.ref#2 (bằng cách giúp đỡ của http://eel.is/c++draft/dcl.init#dcl.init.list-3 "Ngược lại, nếu T là một loại tài liệu tham khảo, một tạm thời prvalue của các loại tham chiếu bởi T là sao chép danh sách -initialized ... ") rồi đến http://eel.is/c++draft/over.best.ics#over.ics.list-6. Ngữ cảnh OR kết quả có các ứng cử viên B(A);B(const B&), với đối số 0. Đây là ngữ cảnh OR thứ hai của chúng ta và là khởi tạo danh sách sao chép bằng 13.3.1.7 (theo yêu cầu của over.ics.ref # 2 và dcl.init.list-3).

      • Đối B(A), các nhà xây dựng là rõ ràng và do đó bỏ qua bởi Clang (không phù hợp với spec) nhưng chấp nhận bởi GCC (do đó sự nhập nhằng).

      • Đối với B(const B&), đây là trường hợp được xử lý bởi @Columbo và do đó chuyển đổi do người dùng xác định cần thiết bị cấm. Bản nháp mới hơn không còn quy tắc này nữa (nhưng có thể nó sẽ được thêm lại). Nhưng vì 0 đến const B& sẽ là một chuyển đổi do người dùng xác định bình thường (không phải là khởi tạo danh sách), nó sẽ bỏ qua hàm khởi tạo rõ ràng cần thiết cho chuyển đổi đó (cho lần sử dụng thứ hai này của hàm tạo bản sao) và do đó do người dùng xác định chuyển đổi sẽ không thể nào được nêu ra anyway và các quy tắc có ý nghĩa ít hơn nhiều hơn tôi nghĩ khi tôi đã viết tóm tắt ngắn hơn ở trên.

Vì vậy cho GCC nó có thể sử dụng các nhà xây dựng rõ ràng trực tiếp, và ngoài ra bởi việc sử dụng duy nhất của các nhà xây dựng bản sao. Đối với clang, nó chỉ xem xét việc sử dụng trực tiếp constructor rõ ràng, và nó sẽ không sử dụng nó gián tiếp bởi một bản sao-list-initialization bằng cách sử dụng constructor sao chép như GCC. Cả hai sẽ không cân nhắc việc sử dụng hàm tạo bản sao lần thứ hai và nó không liên quan ở đây.

2

Các đúng ngữ nghĩa danh sách khởi tạo là

B b{0}; 

mà biên dịch tốt. Nếu bạn viết B b({0});, gcc không thể quyết định xem có gọi số B(A) trực tiếp hoặc tạo B ({0}) và sau đó sao chép nó bằng B(const B&) trong giai đoạn thứ hai. Không có thứ tự ưu tiên giữa hai tùy chọn này.

Đó là vấn đề ngôn ngữ, không phải vấn đề của trình biên dịch. Xem này gcc bug report.

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