2015-02-26 18 views
5

tại sao mã sau không biên dịch và khi tôi xóa Từ khóa rõ ràng trước hàm tạo trong lớp A, nó biên dịch?Ảnh hưởng của các nhà thầu "rõ ràng" trong quá trình phân giải quá tải

Sử dụng Visual Studio 2013:

enum E { e1_0, e1_1 }; 

template<typename T> 
struct A 
{ 
    A() {} 
    explicit A(unsigned long) {} 
    A(T) {} 
}; 

struct B 
{ 
    B() {} 
    B(E) {} 
}; 


void F(B) {}; 
void F(A<short>) {}; 

void test() 
{ 
    F(e1_0); 
} 

Lỗi:

1>------ Build started: Project: exp_construct_test, Configuration: Debug Win32 ------ 
1> exp_construct_test.cpp 
1>e:\exp_construct_test\exp_construct_test.cpp(23): error C2668: 'F' : ambiguous call to overloaded function 
1>   e:\exp_construct_test\exp_construct_test.cpp(19): could be 'void F(A<short>)' 
1>   e:\exp_construct_test\exp_construct_test.cpp(18): or  'void F(B)' 
1>   while trying to match the argument list '(E)' 
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== 

Edit: tôi tải kêu vang và biên soạn với kêu vang-cl mà báo cáo lỗi cho cả hai trường hợp. Vì vậy, như đã được chỉ ra trong bình luận, sự mơ hồ là giữa A<short>(short)B(E). Vì vậy, có lẽ có một lỗi trong VC + +, rằng khi tôi loại bỏ explicit từ A(unsigned long), trình biên dịch bởi bất cứ ý định chọn B (E) thay vì để nâng cao một lỗi ambiguouity. Quay lại đầu trang | Bất cứ ai có thể xác nhận hành vi kêu vang như tuân thủ tiêu chuẩn, và VC + + như lỗi?

tôi thêm

void G(E) {}; 
void G(short) {}; 

và một lời kêu gọi G như thế này:

G(e1_0); 

Mà không tăng bất kỳ lỗi. Tại sao ở đây G(E) được đặt trước và trong trường hợp các ứng viên A<short>::A(short)B::B(E), chúng mơ hồ?

End Sửa

Cảm ơn --joja

+2

Nếu tôi xóa 'tường minh', nó cũng không biên dịch, cũng không được. Các cuộc gọi mơ hồ không phải là 'A :: A' nhưng để 'F'. Lưu ý rằng 'A :: A (T)' không phải là 'tường minh', do đó cuộc gọi đến' F' là mơ hồ. Nếu bạn thêm * '' '' vào 'A :: A (T)', nó sẽ loại trừ chuyển đổi đó và mã sẽ biên dịch. – 5gon12eder

+0

Từ khóa rõ ràng ở phía trước của một hàm tạo ngăn một đối tượng thuộc loại đó được khởi tạo ngầm với chỉ một tham số sẽ hoạt động như một đối số hàm tạo. Khi bạn xóa 'tường minh' khỏi hàm tạo của A, bạn có thể xây dựng A đơn giản bằng cách truyền 'void F (A )' một đoạn ngắn, đó là những gì F (e1_0) dường như đang làm – Prismatic

+0

@ 5gon12eder, trình biên dịch nào bạn đang sử dụng? Trong VS2013 nó biên dịch mà không có 'tường minh'. Tôi biết, rằng các cuộc gọi mơ hồ là 'F', nhưng nguyên nhân là việc chuyển đổi' e1_0' thành một loại mà quá tải của 'F' chấp nhận. Khi 'clear' được loại bỏ, trình biên dịch chọn' B (E) 'để thực hiện chuyển đổi và gọi' F (B) '. Tôi đã thêm 'rõ ràng' để chắc chắn, rằng tôi không ngầm sử dụng hàm tạo, mà là một trường hợp đặc biệt chỉ có hàm tạo trong mã thực của tôi. Theo ý kiến ​​của tôi, việc sử dụng 'rõ ràng' thậm chí sẽ hướng dẫn trình biên dịch sử dụng chuyển đổi B (E) nhiều hơn và không xem xét một' enum E' thành 'unsigned long' promotion. – joja

Trả lời

3

Hãy nhìn vào các biến thể khác nhau của các ví dụ của bạn một sau khi khác.

  1. Ví dụ ban đầu gọi f(e0).

    enum E {e0, e1}; 
    
    template<typename T> 
    struct A 
    { 
        A(); // (1) 
        explicit A(unsigned long); // (2) 
        A(T); // (3) 
    }; 
    
    struct B 
    { 
        B(); // (4) 
        B(E); // (5) 
    }; 
    
    void f(A<short>); // (6) 
    void f(B); // (7) 
    
    void g(E); // (8) 
    void g(short); // (9) 
    

    Trong ba khả năng

    • chuyển đổi e0 một unsigned long, tạo ra một A<short> từ nó thông qua constructor (2) và gọi tình trạng quá tải (6),
    • chuyển đổi e0-short, tạo ra một A<hort> từ nó thông qua hàm tạo (3) và quá tải cuộc gọi (6) và
    • tạo một B từ e0 qua hàm tạo (5) và gọi quá tải (7)

    tùy chọn đầu tiên không áp dụng được vì (2) là explicit. Hai cái còn lại đều liên quan đến một chuyển đổi do người dùng định nghĩa, được coi là tốt và không được chuyển đổi. Cuộc gọi không rõ ràng và chương trình không đúng định dạng.

  2. Hãy xóa explicit khỏi hàm tạo và gọi f(e0).

    template<typename T> 
    struct A 
    { 
        A(); // (1) 
        A(unsigned long); // (2) 
        A(T); // (3) 
    }; 
    
    struct B 
    { 
        B(); // (4) 
        B(E); // (5) 
    }; 
    

    Ba tùy chọn vẫn như cũ nhưng lần này, cả ba đều áp dụng và gọi là (thậm chí hơn) mơ hồ và chương trình vô hình thành.

  3. Hãy tạo cả hai hàm tạo explicit và gọi f(e0).

    template<typename T> 
    struct A 
    { 
        A(); // (1) 
        explicit A(unsigned long); // (2) 
        explicit A(T); // (3) 
    }; 
    
    struct B 
    { 
        B(); // (4) 
        B(E); // (5) 
    }; 
    

    Điều này làm cho nó không thể xây dựng một cách rõ ràng A<short> và cuộc gọi rõ ràng là quá tải (5).

  4. Hãy tạo một hàm tạo của Bexplicit và gọi f(e0).

    template<typename T> 
    struct A 
    { 
        A(); // (1) 
        explicit A(unsigned long); // (2) 
        explicit A(T); // (3) 
    }; 
    
    struct B 
    { 
        B(); // (4) 
        explicit B(E); // (5) 
    }; 
    

    Lần này, none trong ba đường dẫn chuyển đổi được áp dụng bởi vì mỗi người ta sẽ đi qua một constructor explicit. Không có tình trạng quá tải của f được áp dụng và chương trình bị hỏng.

  5. Gọi g(e0).

    Chúng tôi có hai khả năng ở đây:

    • Gọi quá tải (8) mà không cần bất kỳ chuyển đổi hoặc
    • chuyển đổi e0 đến một short và gọi số (9) quá tải.

    Trong số hai tùy chọn này, tùy chọn đầu tiên thuận lợi rõ ràng vì nó không liên quan đến chuyển đổi. Cuộc gọi không rõ ràng. (Thậm chí nếu nhà xây dựng (5) không explicit là.)

Lưu ý rằng các nhà thầu mặc định (1) và (4) thực sự không đóng góp bất cứ điều gì để cuộc thảo luận này. Thử nghiệm với GCC 4.9.1, tất cả năm ví dụ đều hoạt động như mong đợi.

+0

Cảm ơn, @ 5gon12eder, cho câu trả lời này. Tôi có một số nhận xét: - Điểm 2: với hàm tạo (2) ** không ** được đánh dấu rõ ràng, không phải trong VisualC, cũng không phải clang tôi nhận được ba tình trạng quá tải có thể được báo cáo. - Điểm 1 .: Với (2) rõ ràng, gọi f (e0) tôi mong đợi (5) để được gọi vì chỉ người dùng định nghĩa hàm tạo chuyển đổi, E -> B (E) được sử dụng, cho (3) là cần thiết bước khác: E -> short -> A (viết tắt). - Vì vậy, bạn xác nhận, Visual C++ là lỗi khi biên dịch ví dụ về điểm 2. mà không tăng bất kỳ thông báo lỗi và âm thầm gọi B (E)? – joja

+0

@joja ** ad point 1: ** Điều này có vẻ trực quan với bạn nhưng theo như tôi có thể nói, không có quy tắc trong tiêu chuẩn xếp hạng bất kỳ trình tự chuyển đổi nào tốt hơn so với lệnh kia vì vậy cuộc gọi không rõ ràng. ** ad point 2: ** Tôi sẽ không vội vàng tuyên bố rằng một trình biên dịch mà tôi không truy cập có lỗi nhưng tôi khá tự tin rằng lý do của tôi và hành vi của GCC là chính xác ở đây. Có lẽ một luật sư ngôn ngữ nâng cao hơn sẽ đến và bình luận với nhiều thẩm quyền hơn về vấn đề này. – 5gon12eder

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