2013-02-20 27 views
12

xem xét ví dụ đơn giản sau đâyquá tải chuyển đổi hành mẫu

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
    operator bool() {return false;} 
}; 

int main() 
{ 
    C c; 
    double x = c; 
    std::cout << x << std::endl; 
} 

Khi biên soạn với Clang, nó mang lại cho các lỗi sau

test.cpp:11:12: error: conversion from 'C' to 'double' is ambiguous 
    double x = c; 
     ^ ~ 
test.cpp:4:5: note: candidate function 
    operator int() {return 1;} 
    ^
test.cpp:5:5: note: candidate function 
    operator bool() {return false;} 
    ^
test.cpp:3:27: note: candidate function [with T = double] 
    template <typename T> operator T() {return 0.5;} 
         ^
1 error generated. 

trình biên dịch khác tạo ra các lỗi tương tự, ví dụ, GCC và Intel iclc

Nếu tôi xóa operator intoperator bool. Nó biên dịch tốt và hoạt động như mong đợi. Nếu chỉ xóa một trong số đó, hãy giữ nguyên toán tử mẫu và nói operator int, thì phiên bản không phải mẫu luôn được chọn.

Sự hiểu biết của tôi là chỉ khi các hàm mẫu quá tải và mẫu không bằng nhau theo nghĩa là cả hai khớp hoàn toàn hoặc cả hai yêu cầu cùng một chuỗi chuyển đổi, phiên bản không phải mẫu sẽ được ưu tiên. Tuy nhiên trong trường hợp này, có vẻ như trình biên dịch không thấy mẫu toán tử như là một đối sánh hoàn hảo. Và khi cả hai quá trình tải xuống boolint đều xuất hiện, thì tự nhiên nó coi chúng là mơ hồ.

Tóm lại, câu hỏi của tôi là tại sao mẫu toán tử không được coi là khớp hoàn hảo trong trường hợp này?

Trả lời

4

Điều này thật thú vị. Có hai cách để đọc một phần quan trọng trong phần 13.3.3.Ví dụ ban đầu chắc chắn sẽ gọi mẫu chức năng, nhưng phiên bản mà một trong những mẫu không được xóa có thể được lập luận là mơ hồ.

13.3.3:

Một chức năng hữu hiệu đối F1 được định nghĩa là một chức năng tốt hơn so với một chức năng hữu hiệu đối F2 nếu cho tất cả các đối i, ICS_i (F1) không phải là một chuỗi chuyển đổi tồi tệ hơn hơn ICS_i (F2), và sau đó

  • đối với một số lập luận j, ICS_j (F1) là một chuỗi chuyển đổi tốt hơn ICS_j (F2), hoặc, nếu không muốn nói rằng,

  • bối cảnh là một khởi bởi chuyển đổi người dùng định nghĩa (xem 8.5, 13.3.1.5 và 13.3.1.6) và chuỗi chuyển đổi chuẩn từ kiểu trả về F1 thành loại đích (nghĩa là loại thực thể được khởi tạo) là chuỗi chuyển đổi tốt hơn chuỗi chuyển đổi chuẩn từ kiểu trả về F2 thành loại đích, hoặc, nếu không có điều đó,

  • F1 là chức năng không phải mẫu và F2 là chuyên môn về mẫu chức năng hoặc, nếu không,

  • F1F2 là các chuyên môn mẫu chức năng và mẫu chức năng cho F1 chuyên biệt hơn mẫu cho F2 theo các quy tắc đặt hàng một phần được mô tả trong 14.5.6.2.

Nếu có chính xác một hàm khả thi là chức năng tốt hơn tất cả các chức năng khả thi khác, thì đó là chức năng được chọn bởi độ phân giải quá tải; nếu không cuộc gọi bị hình thành không đúng.

Trong ví dụ này, kêu vang xác định một cách chính xác các bộ ba chức năng ứng cử viên khả thi:

C::operator int() 
C::operator bool() 
C::operator double<double>() 

Thứ ba là một hàm template đặc biệt. (Tôi không nghĩ cú pháp ở trên là hợp pháp, nhưng bạn có ý tưởng: tại thời điểm này, độ phân giải quá tải không được coi là mẫu, mà là một chuyên môn với kiểu chức năng xác định.)

Chuyển đổi ẩn Trình tự đối số tại đây (ICS1) là đối sánh chính xác "lvalue C" thành "C&" trên thông số ngầm, do đó sẽ không tạo sự khác biệt.

Ví dụ này chính xác là tình huống được mô tả trong dấu đầu dòng thứ hai, do đó hàm trả về double rõ ràng là tốt hơn so với hai ví dụ khác.

Đây là nơi nó trở nên lạ: Bằng cách đọc theo nghĩa đen, operator int cũng tốt hơn so với chuyên môn mẫu, vì dấu đầu dòng thứ ba. "Chờ một phút, không nên 'tốt hơn' là đối xứng? Làm sao bạn có thể nói F1 tốt hơn F2F2 là tốt hơn F1?" Thật không may, tiêu chuẩn không rõ ràng nói bất cứ điều gì sắp xếp. "Không phải viên đạn thứ hai có ưu tiên hơn viên đạn thứ ba vì cụm từ 'nếu không phải' không?"Vâng, cho liên tục F1F2. Nhưng tiêu chuẩn không nói rằng thỏa mãn đạn thứ hai cho (F1,F2) làm cho viên đạn thứ ba cho (F2,F1) không được áp dụng.

Tất nhiên, kể từ khi operator int không phải là tốt hơn so với operator bool và ngược lại, vẫn có "chính xác một chức năng khả thi, đó là một chức năng tốt hơn tất cả các chức năng khả thi khác"

Tôi không chính xác ủng hộ việc đọc lạ này, ngoại trừ có thể báo cáo nó như là một lỗi tiêu chuẩn. hậu quả (như xóa quá tải là không phải tốt nhất từ ​​ví dụ này thay đổi một chương trình từ tốt thành lập đến mơ hồ!). Tôi nghĩ ý định là cho viên đạn thứ hai được xem xét cả hai cách trước khi viên đạn thứ ba được xem xét.

Điều đó có nghĩa là mẫu chức năng sẽ được chọn theo độ phân giải quá tải và đây là lỗi clang.

+0

"Đọc lạ" của bạn về tiêu chuẩn rất thú vị và thực sự có ý nghĩa rất nhiều với tôi. Nếu tôi hiểu nó một cách chính xác, cho dù nhà điều hành int là tốt hơn so với các mẫu phụ thuộc vào đó là F1 và đó là F2. Nếu chúng ta xem F1 = op int, F2 = op , thì tại dấu 3 chúng ta xác định F1 tốt hơn F2 và dừng lại. Nhưng nếu chúng ta xem xét F1 = op , F2 = op int, sau đó tại bullet 2 chúng tôi xác định F1 là tốt hơn và dừng lại. Nhưng nếu có ba trong số họ, sau đó op int và op bool không còn tốt hơn so với những người khác, và do đó không mơ hồ –

6

Hãy phá vỡ này xuống thành hai vấn đề khác nhau:

1. Tại sao điều này tạo ra một lỗi biên dịch?

struct C 
{ 
    operator bool() {return false;} 
    operator int() {return 1;} 
}; 

Khi cả hai intbool có thể được ngầm chuyển đổi sang double, trình biên dịch không thể biết được chức năng đó nên sử dụng. Có hai chức năng mà nó có thể sử dụng và không ai được ưu tiên hơn một chức năng khác.

2. Tại sao phiên bản được tô điểm không phù hợp?

struct C 
{ 
    template <typename T> operator T() {return 0.5;} 
    operator int() {return 1;} 
}; 

Tại sao operator int() được gọi khi yêu cầu tăng gấp đôi?

Chức năng không phải mẫu được gọi vì chức năng không phải mẫu được ưu tiên trong độ phân giải quá tải. (Overloading function templates)

EDIT: tôi đã sai! Như Yan Zhou đã đề cập trong bình luận của mình, và như nó được nêu trong liên kết tôi đã cung cấp, một trận đấu hoàn hảo trong chức năng khuôn mẫu được ưu tiên hơn chức năng không có khuôn mẫu.

Tôi đã kiểm tra mã của bạn (được biên dịch bằng g ++ 4.7.2) và nó hoạt động như mong đợi: nó trả lại 0.5, nói cách khác, chức năng templated đã được sử dụng!

EDIT2: Bây giờ tôi đã thử với tiếng kêu và tôi có thể tái tạo hành vi mà bạn mô tả. Vì nó hoạt động chính xác trong gcc, điều này có vẻ là một lỗi trong clang.

+0

Phần đầu tiên tôi đã tìm ra như được nêu trong câu hỏi. Tuy nhiên phần thứ hai, chức năng không phải mẫu chỉ được ưu tiên trong độ phân giải quá tải khi các khía cạnh khác bằng nhau. Trong trường hợp này, khuôn mẫu không yêu cầu chuyển đổi, nó trả về một đôi trong khi toán tử không phải mẫu trả về int hoặc bool và sau đó cần phải được chuyển đổi thành gấp đôi. Vì vậy, tôi vẫn không hiểu tại sao nó không phải là một trận đấu hoàn hảo. Ngay cả trong liên kết bạn đưa ra, lệnh gọi thứ hai tới 'f' cho thấy rằng một mẫu đối sánh hoàn hảo được ưu tiên. Non -emplate được ưu tiên là cuộc gọi đầu tiên và thứ ba –

+0

@YanZhou bạn nói đúng, tôi đã cập nhật câu trả lời của mình. Nhưng ứng dụng của tôi dường như có một hành vi khác với hành vi của bạn. Tôi đã thử nghiệm nó với gcc, và hiện đang cài đặt clang để so sánh. – Misch

+0

Phiên bản GCC nào bạn đã sử dụng? Tôi đã thử G ++ 4,7 và nó cho cùng một lỗi như Clang, do đó, Intel icpc 13.1 –

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