Hãy xem xét hai loại sau đây:Gọi để điều hành chuyển đổi thay vì chuyển đổi constructor trong C++ 17 trong giải quyết tình trạng quá tải
#define PRETTY(x) (std::cout << __PRETTY_FUNCTION__ << " : " << (x) << '\n')
struct D;
struct C {
C() { PRETTY(this);}
C(const C&) { PRETTY(this);}
C(const D&) { PRETTY(this);}
};
struct D {
D() { PRETTY(this);}
operator C() { PRETTY(this); return C();}
};
Chúng tôi quan tâm trong việc giải quyết tình trạng quá tải giữa hai cấu trúc:
C::C(const C&);
C::C(const D&);
Mã này hoạt động như mong đợi:
void f(const C& c){ PRETTY(&c);}
void f(const D& d){ PRETTY(&d);}
/*--------*/
D d;
f(d); //calls void f(const D& d)
từ void f(const D& d)
là kết quả phù hợp hơn.
Nhưng:
D d;
C c(d);
(như bạn có thể nhìn thấy here)
cuộc gọi D::operator C()
khi biên dịch với std=c++17
, và kêu gọi C::C(const D&)
với std=c++14
trên cả hai kêu vang và ĐẦU gcc. Hơn nữa, hành vi này đã thay đổi gần đây: với clang 5, C::C(const D&)
được gọi với cả hai std=c++17
và std=c++14
.
Hành vi chính xác ở đây là gì?
đọc dự kiến của tiêu chuẩn (dự thảo mới nhất N4687):
C c(d)
là một trực khởi mà không phải là một sự bỏ bớt bản sao ([dcl.init] /17.6.1). [dcl.init] /17.6.2 cho chúng ta biết rằng các nhà thầu có thể áp dụng được liệt kê và cái tốt nhất được chọn bởi độ phân giải quá tải. [over.match.ctor] cho chúng ta biết rằng các nhà xây dựng có thể áp dụng được trong trường hợp này là tất cả các nhà xây dựng.
Trong trường hợp này: C()
, C(const C&)
và C(const D&)
(không di chuyển ctor). C()
rõ ràng là không khả thi và do đó bị loại bỏ khỏi tập quá tải. ([over.match.viable])
Các nhà xây dựng không có tham số đối tượng ẩn và do đó, C(const C&)
và C(const D&)
cả hai đều lấy chính xác một tham số. ([over.match.funcs]/2)
Bây giờ chúng tôi truy cập [over.match.best]. Ở đây chúng tôi thấy rằng chúng tôi cần xác định xem của hai trình tự chuyển đổi tiềm ẩn (ICS) nào tốt hơn. ICS của C(const D&)
chỉ liên quan đến chuỗi chuyển đổi chuẩn, nhưng ICS của C(const C&)
liên quan đến chuỗi chuyển đổi do người dùng xác định.
Do đó, C(const D&)
phải được chọn thay vì C(const C&)
.
Điều thú vị là hai thay đổi này cả hai sẽ làm cho "đúng" constructor để được gọi là:
operator C() { /* */ }
vào operator C() const { /* */ }
hoặc
C(const D&) { /* */ }
vào C(D&) { /* */ }
Đây là những gì sẽ xảy ra (tôi nghĩ) trong bản sao trường hợp alization trong đó chuyển đổi do người dùng xác định và các nhà xây dựng chuyển đổi có thể bị quá tải độ phân giải .
Như Columbo khuyến Tôi nộp một báo cáo lỗi với gcc và kêu vang
gcc lỗi https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82840
kêu vang lỗi https://bugs.llvm.org/show_bug.cgi?id=35207
Nhận xét không dành cho thảo luận mở rộng; cuộc hội thoại này đã được [chuyển sang trò chuyện] (http://chat.stackoverflow.com/rooms/158716/discussion-on-answer-by-columbo-call-to-conversion-operator-instead-of-convertin). – Andy