2012-12-31 18 views
5

Đầu ra của chương trình sau ...Trình xây dựng mẫu có quyền ưu tiên trên bản sao bình thường và di chuyển constructor?

#include <iostream> 

using namespace std; 

struct X 
{ 
    X(const X&)    { cout << "copy" << endl; } 
    X(X&&)     { cout << "move" << endl; } 

    template<class T> X(T&&) { cout << "tmpl" << endl; } 
}; 

int main() 
{ 
    X x1 = 42; 
    X x2(x1); 
} 

tmpl 
tmpl 

Kết quả mong muốn là:

tmpl 
copy 

Tại sao không phải là constructor sao chép cụ thể được ưu tiên hơn các constructor mẫu?

Vẫn còn cách khắc phục để sao chép và di chuyển quá tải của hàm tạo sẽ được ưu tiên hơn hàm tạo mẫu không?

+0

Đây là lớp cuối cùng liên quan đến câu hỏi này: http://codereview.stackexchange.com/questions/20058/a-c11-any-class –

Trả lời

2

Quy tắc xử lý quá tải bình thường vẫn áp dụng khi chọn hàm tạo - và hàm tạo tham chiếu không tham số lvalue (đối với hàm tạo mẫu sau khi khấu trừ đối số) là kết hợp tốt hơn so với hàm tạo tham chiếu const lvalue.

Bạn có thể, tất nhiên, chỉ cần thêm một tình trạng quá tải khác tham gia một tham chiếu không phải lvalue, tức là

X(X&)    { cout << "copy" << endl; } 

Cập nhật: Các trường hợp khác mà các mẫu nhà xây dựng là một trận đấu tốt hơn:

const X f() 
{ return X(); } 

struct Y : X 
{ Y() { } }; 

int main() 
{ 
    X x3(f()); // const-qualified rvalue 
    Y y; 
    X x4(y); // derived class 
} 
+0

Tôi cũng cần phải thêm 'X (const X &&)' hoặc là điều này không thể? Có bất kỳ trường hợp nào khác trong đó trình dịch chuyển hoặc sao chép sẽ khớp với sự thiếu sót của hàm tạo mẫu không? –

+0

@AndrewTomazosFathomlingCorps: SFINAE có thể giúp bạn ở đó, để bạn có thể có cả chữ ký chức năng * nguyên vẹn * (tôi nghĩ điều đó rất quan trọng). Ví dụ, bạn có thể sử dụng 'std :: enable_if' trong mẫu chức năng để chỉ cho phép các trường hợp mong muốn. – Nawaz

+0

Có, nếu bạn có một giá trị const, ví dụ: từ 'const X f();' và 'X x3 (f())'. Sau đó, có trường hợp initializer là một lớp dẫn xuất mà cũng sẽ sử dụng constructor mẫu. – cmeerw

5

Vâng, đó là vì reference-collapsing.

Ở giai đoạn giải quyết tình trạng quá tải, khi hàm mẫu được khởi tạo, T được deduded là X&, vì vậy T&& (đó là X& &&) trở thành X& do tham khảo-sụp đổ, và chức năng khởi tạo từ hàm mẫu trở nên chính xác khớp với số và hàm tạo bản sao yêu cầu chuyển đổi từ X& thành const X& (đó là lý do tại sao nó không được chọn là so khớp kém).

Tuy nhiên, nếu bạn xóa const từ trình tạo bản sao, trình tạo bản sao sẽ được ưu tiên. Hãy thử điều này:

X(/*const*/ X&) { cout << "copy" << endl; } 

Output như mong đợi.

Hoặc cách khác, nếu bạn thực hiện các tham số trong hàm template như const T& sau đó copy-constructor sẽ được gọi (thậm chí nếu nó vẫn giống nhau!), Bởi vì tài liệu tham khảo-sụp đổ sẽ không trở thành hình ảnh bây giờ:

template<class T> X(const T &) { cout << "tmpl" << endl; } 

Output được mong đợi, một lần nữa.

3

Nếu bạn không muốn thêm constructor khác (như câu trả lời khác đề xuất), bạn có thể sử dụng SFINAE để hạn chế các cuộc gọi, bằng cách thay thế mẫu constructor của bạn bằng cách này:

template<class T 
    , typename std::enable_if<not std::is_same<X, typename std::decay<T>::type>::value, int>::type = 0 
> X(T&&) { cout << "tmpl " << endl; } 

nào chỉ liên quan đến việc bổ sung thêm một templ dummy mặc định ate đối số (một kỹ thuật đã biết: http://www.boost.org/doc/libs/1_52_0/libs/utility/enable_if.html). Không có tiêu đề bổ sung nào là cần thiết.

Bạn sẽ nhận được kết quả mong muốn.

Tôi nhận được câu trả lời này từ một vấn đề liên quan: http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html. Tất cả điều này có vẻ không phù hợp, nhưng dường như là cách duy nhất hiện nay. Tôi vẫn muốn thấy một giải pháp thanh lịch hơn.

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