Tôi tin rằng Clang là chính xác ở đây. GCC không nên chấp nhận mã.
Lý do là cách giải quyết quá tải cho các hàm tạo cho bản sao đối tượng xảy ra trong một return
câu lệnh được chỉ định trong [class.copy] p32
(mỏ nhấn mạnh):
Khi các tiêu chí để loại bỏ một hàm tạo sao chép / di chuyển được đáp ứng, [...] và đối tượng được sao chép được chỉ định bởi một giá trị, [...], việc phân giải quá tải để chọn hàm tạo cho bản sao được thực hiện đầu tiên như thể đối tượng được chỉ định bởi một giá trị. Nếu giải quyết quá tải đầu tiên không thành công hoặc không được thực hiện hoặc nếu kiểu của tham số đầu tiên của hàm tạo đã chọn không phải là tham chiếu rvalue cho kiểu của đối tượng (có thể đủ điều kiện cv) , thì giải quyết quá tải sẽ được thực hiện lại, coi đối tượng là một giá trị.
Trong ví dụ này, các tiêu chí về loại bỏ được đáp ứng (bởi dấu đầu dòng đầu tiên [class.copy] p31
) và đối tượng được sao chép được chỉ định bởi một giá trị, vì vậy đoạn này áp dụng.
Giải quyết quá tải lần đầu tiên được cố gắng như thể đối tượng được chỉ định bởi một giá trị. Các hàm explicit
tạo không phải là ứng cử viên (xem bên dưới để giải thích lý do tại sao), vì vậy hàm Derived(Base&&)
tạo được chọn. Tuy nhiên, điều này nằm trong "loại tham số đầu tiên của phương thức khởi tạo đã chọn không phải là tham chiếu rvalue cho kiểu của đối tượng" (thay vào đó, nó là tham chiếu rvalue cho kiểu của lớp cơ sở của đối tượng), do đó, việc phân giải quá tải phải được thực hiện lại , coi đối tượng như một giá trị.
Giải pháp quá tải thứ hai này không thành công, bởi vì hàm tạo khả thi duy nhất (một lần nữa, các hàm explicit
tạo không phải là ứng cử viên) có tham số tham chiếu rvalue, không thể liên kết với giá trị. Clang hiển thị lỗi thất bại giải quyết quá tải kết quả.
Để hoàn thành lời giải thích, đây là lý do tại sao các hàm explicit
tạo không phải là ứng cử viên cho giải pháp quá tải (tất cả sự nhấn mạnh là của tôi).
Đầu tiên, hãy [dcl.init] p15
nói rằng:
Việc khởi tạo xảy ra ở dạng = của bộ khởi tạo hoặc điều kiện dấu ngoặc nhọn (6.4), cũng như trong truyền đối số, trả về hàm , ném ngoại lệ (15.1), xử lý ngoại lệ (15.3) và tổng hợp thành viên khởi tạo (8.5.1), được gọi là sao chép-khởi tạo . "
Tiếp theo, chúng ta xem xét [over.match.ctor] p1
:
Đối với khởi tạo sao chép , các hàm ứng cử viên là tất cả các hàm tạo chuyển đổi (12.3.1) của lớp đó.
Cuối cùng, chúng ta thấy rằng các hàm explicit
tạo không chuyển đổi các hàm tạo trong [class.conv.ctor] p1
:
Một phương thức khởi tạo được khai báo mà không có hàm-specifier chỉ explicit
định một chuyển đổi từ các kiểu tham số của nó thành kiểu của lớp của nó. Một hàm tạo như vậy được gọi là một hàm tạo chuyển đổi .