Mã đầu tiên, sau đó điều tôi nghĩ sẽ xảy ra. (Trong phần tiếp theo, tôi sẽ bỏ qua tham số đầu tiên, vì chúng ta chỉ quan tâm đến tham số thứ hai. Tham số đầu tiên luôn là đối sánh chính xác trong ví dụ của bạn). Xin lưu ý rằng các quy tắc hiện đang ở thông lượng trong spec, vì vậy tôi sẽ không nói rằng một hoặc trình biên dịch khác có lỗi.
B1 b1a(x, {y});
Mã này không thể gọi const Y&
constructor trong C++ 11, vì Y
là một tổng hợp và Y
không có thành viên dữ liệu kiểu Y
(tất nhiên) hay cái gì khác initializable bởi nó (đây là một cái gì đó xấu xí, và được làm việc trên để được cố định - CD C++ 14 không có từ ngữ cho điều này được nêu ra, vì vậy tôi không chắc chắn cho dù C + + cuối cùng 14 sẽ chứa sửa chữa này).
Hàm khởi tạo có tham số const A<Y>&
có thể được gọi - {y}
sẽ được lấy làm đối số cho hàm tạo A<Y>
và sẽ khởi tạo hàm xây dựng std::initializer_list<Y>
của hàm tạo.
Do đó - hàm tạo thứ hai được gọi là thành công.
B1 b1b(x, {{y}});
Ở đây, đối số cơ bản giống nhau được tính cho hàm tạo với thông số const Y&
.
Đối với hàm tạo có kiểu tham số const A<Y>&
, nó phức tạp hơn một chút. Quy tắc cho chi phí chuyển đổi trong tính toán độ phân giải quá tải, chi phí khởi tạo std::initializer_list<T>
yêu cầu mọi thành phần của danh sách có thể chuyển đổi thành T
. Tuy nhiên, trước đây chúng tôi đã nói rằng không thể chuyển đổi {y}
thành Y
(vì nó là tổng hợp). Điều quan trọng là phải biết liệu std::initializer_list<T>
có phải là tổng hợp hay không. Thành thật mà nói, tôi có không có ý tưởng có hay không nó phải được coi là tổng hợp theo các điều khoản của thư viện chuẩn.
Nếu chúng tôi coi đó là không tổng hợp, thì chúng tôi sẽ xem xét hàm tạo bản sao của std::initializer_list<Y>
, tuy nhiên lại kích hoạt cùng một chuỗi các phép thử (dẫn đến "đệ quy vô hạn" trong kiểm tra độ phân giải quá tải).Vì điều này khá lạ và không thể thực hiện được, tôi không nghĩ rằng bất kỳ việc triển khai nào thực hiện đường dẫn này.
Nếu chúng tôi lấy std::initializer_list
làm tổng hợp, chúng tôi sẽ nói "không, không tìm thấy chuyển đổi" (xem vấn đề tổng hợp ở trên). Trong trường hợp đó vì chúng ta không thể gọi hàm tạo khởi tạo với danh sách khởi tạo đơn như một tổng thể, {{y}}
sẽ được chia thành nhiều đối số và (các) hàm tạo của A<Y>
sẽ lấy riêng từng đối số đó. Do đó, trong trường hợp này, chúng tôi sẽ kết thúc bằng việc {y}
khởi tạo một tham số std::initializer_list<Y>
- một tham số hoàn toàn ổn định và hoạt động như một nét duyên dáng.
Vì vậy, theo giả định rằng std::initializer_list<T>
là tổng hợp, điều này là tốt và gọi hàm tạo thứ hai thành công.
B2 b2a(x, {P<Y>(y)});
Trong trường hợp này và các trường hợp sau, chúng tôi không có vấn đề tổng hợp như trên với Y
nữa, vì P<Y>
có một constructor dùng cung cấp.
Đối với hàm tạo tham số P<Y>
, tham số đó sẽ được khởi tạo bởi {P<Y> object}
. Vì P<Y>
không có danh sách khởi tạo, danh sách sẽ được chia thành các đối số riêng lẻ và gọi hàm khởi tạo của P<Y>
với đối tượng giá trị là P<Y>
.
Đối với các nhà xây dựng tham số A<P<Y>>
, nó cũng giống như trường hợp trên với A<Y>
khởi tạo bởi {y}
: Kể từ std::initializer_list<P<Y>>
có thể được khởi tạo bởi {P<Y> object}
, danh sách đối số không chia, và do đó niềng răng được sử dụng để khởi tạo mà constructor CỦA std::initializer_list<T>
.
Bây giờ, cả hai nhà thầu đều hoạt động tốt. Chúng hoạt động giống như các hàm quá tải ở đây và tham số thứ hai của chúng trong cả hai trường hợp yêu cầu chuyển đổi do người dùng xác định. Các chuỗi chuyển đổi do người dùng xác định chỉ có thể được so sánh nếu trong cả hai trường hợp cùng một hàm chuyển đổi hoặc hàm tạo được sử dụng - không phải trường hợp ở đây. Do đó, điều này là mơ hồ trong C++ 11 (và trong CD C++ 14).
Lưu ý rằng ở đây chúng tôi có một điểm tinh tế để khám phá
struct X { operator int(); X(){/*nonaggregate*/} };
void f(X);
void f(int);
int main() {
X x;
f({x}); // ambiguity!
f(x); // OK, calls first f
}
truy cập này kết quả trực quan có thể sẽ được cố định trong thời gian cùng với sửa chữa các weirdness tổng hợp-khởi nêu trên (cả hai sẽ gọi là người đầu tiên f) . Điều này được thực hiện bằng cách nói rằng {x}->X
trở thành một chuyển đổi nhận dạng (như là X->x
). Hiện tại, đó là chuyển đổi do người dùng xác định.
Vì vậy, sự mơ hồ ở đây.
B2 b2b(x, {{P<Y>(y)}});
Đối với các nhà xây dựng với tham số const P<Y>&
, chúng tôi một lần nữa chia các đối số và nhận {P<Y> object}
tranh luận truyền cho constructor (s) của P<Y>
. Hãy nhớ rằng P<Y>
có một hàm tạo bản sao. Nhưng sự phức tạp ở đây là chúng ta không được phép sử dụng nó (xem 13.3.3.1p4), bởi vì nó sẽ yêu cầu một chuyển đổi do người dùng xác định. Nhà xây dựng duy nhất còn lại là người xây dựng Y
, nhưng không thể khởi tạo Y
bằng cách {P<Y> object}
.
Đối với các nhà xây dựng với tham số A<P<Y>>
, các {{P<Y> object}}
có thể khởi tạo một std::initializer_list<P<Y>>
, vì {P<Y> object}
là mui trần để P<Y>
(trừ với Y
trên - dang, uẩn).
Vì vậy, hàm tạo thứ hai được gọi là thành công.
Tóm tắt thông tin cho tất cả 4
- constructor thứ hai được gọi là thành công
- theo giả định rằng
std::initializer_list<T>
là một tổng hợp, điều này là tốt và gọi các nhà xây dựng thứ hai thành công
- sự mơ hồ ở đây
- constructor thứ hai được gọi là thành công
Hai biến cục bộ với tên 'b2b' trong hàm 'f' chỉ là một lỗi đánh máy và không phải là nguyên nhân của vấn đề, tôi giả sử ... – astraujums
Thật vậy! Cố định ... –
Dường như với tôi như tạo một 'A' từ một initializer_list và một' P' từ một 'T' cả hai nên được kết hợp chính xác, do đó sự mơ hồ. Những gì tôi không thể giải thích là tại sao các trình biên dịch lại chọn những trình biên dịch khác nhau để khiếu nại. –