10

Trong đoạn mã sau (live on coliru):Implicit ưu tiên điều hành chuyển đổi

#include <iostream> 
#include <string> 

int main() 
{ 
    struct S { 
     operator bool  () const { return false; } 
     operator std::string() const { return "false"; } 
    } s; 
    std::cout << s << "\n"; // outputs 0 
} 

Làm thế nào để trình biên dịch chọn để chọn chuyển đổi ngầm để bool qua std::string?

Giả thuyết của tôi là trong trường hợp này, nó hoàn toàn có thể là thứ tự khai báo các hương vị khác nhau của std::basic_ostream::operator<<, nhưng có phải tất cả? Liệu tiêu chuẩn có nói gì về việc chọn một chuyển đổi tiềm ẩn cụ thể không?

+2

Chỉ cần để nitpick, không có gì là bao giờ ngầm đúc.Các loại được chuyển đổi hoàn toàn, nhưng * cast * có nghĩa là * chuyển đổi rõ ràng *. – StoryTeller

+0

@StoryTeller Cảm ơn, rất tốt để biết thuật ngữ cụ thể. Tôi đã chỉnh sửa câu hỏi của mình. – YSC

Trả lời

10

Nhớ lại rằng std::string không phải là một loại độc lập, nó thực sự là một chuyên môn mẫu lớp - std::basic_string<char>. Các chi tiết rất quan trọng là sự quá tải khả năng trình chiếu một std::string không mất một cuộc tranh luận std::string const&, nó là một function template rằng suy luận một std::basic_string const&:

template <class CharT, class Traits, class Allocator> 
std::basic_ostream<CharT, Traits>& 
    operator<<(std::basic_ostream<CharT, Traits>& os, 
       const std::basic_string<CharT, Traits, Allocator>& str); 

Template trích không bao giờ xem xét chuyển đổi. Tra cứu tên sẽ tìm thấy mẫu chức năng này, và sau đó loại bỏ tại như là không khả thi do thất bại khấu trừ. S không phải là basic_string<CharT, Traits, Allocator> cho bất kỳ loại nào như vậy, vì vậy chúng tôi đã hoàn tất. Các nhà khai thác luồng khả thi duy nhất sẽ là tất cả những cái tích phân, trong đó bool là kết quả phù hợp nhất.

Nếu có đặc biệt là một chức năng với chữ ký:

std::ostream& operator<<(std::ostream&, std::string const&);  

Sau đó cuộc gọi sẽ là mơ hồ - bạn sẽ nhận được hai chuyển đổi người dùng xác định đó sẽ được xếp hạng tương đương.


này rất dễ dàng để xác minh bằng cách sử dụng các chức năng của chúng ta thay vì triệu quá tải cho operator<<:

void foo(bool); // #1 
void foo(std::string); // #2 

void bar(bool); // #3 
template <class C, class T, class A> 
void bar(std::basic_string<C,T,A>); // #4 

foo(S{}); // error: ambiguous 
bar(S{}); // calls #3 
+0

@YSC: Tôi đã chỉnh sửa của tôi để sửa nó. Tôi đã không nhận ra tại thời điểm nó bây giờ là một phiên bản không hoàn hảo của Barry. Tôi vẫn còn lại nó, bởi vì tôi nghĩ rằng "không làm điều đó, sau đó" lời khuyên là hữu ích. –

+0

@Barry, khấu trừ thành công cho toán tử mẫu <<. Trình biên dịch chọn hàm thành viên vì nó không phải là một khuôn mẫu. Xem câu trả lời của tôi. – Oliv

+0

Một cách rõ ràng để xem điều này cho OP là để nhận xét ra 'toán tử bool' từ mã ban đầu ... sau đó nó không biên dịch (nó không gọi chuyển đổi chuỗi) –

2
ostream& operator<<(bool value); 

Là một hàm thành viên của std::ostream. Mặt khác:

std::ostream& operator<<(std::ostream& os, const std::string& str); 

Là một chức năng độc lập - mà thực sự là tuyên bố như một mẫu. Tham chiếu đến S không khớp với bất kỳ mẫu nào - vì vậy nó không được xem xét để mở rộng mẫu.


Có thể xác định duy nhất tình trạng quá tải nên được chọn, nhưng tôi khuyên bạn không nên làm điều đó.

a) Đây luôn là một trong những góc khó khăn của tiêu chuẩn (do đó bạn có thể gặp lỗi biên dịch;

b) phát triển trong tương lai sẽ luôn tìm thấy mã khó đọc.

Đề xuất của tôi là tránh hoàn toàn sự cố bằng cách chỉ làm cho toán tử chuyển đổi của bạn rõ ràng hoặc cung cấp cho họ tên như to_bool()to_string().

+2

Thành viên/người không phải là thành viên không liên quan. Đó là sự bình dị đó là vấn đề. –

+0

Tôi đồng ý với kết luận của bạn (thích chuyển đổi rõ ràng hơn), nhưng tôi không chắc chắn về giải thích của bạn. Xem [ví dụ này về coliru] (http://coliru.stacked-crooked.com/a/174a94a1e2fdcb7e). – YSC

+0

@ T.C. Ah-ha! Cảm ơn. –

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