2014-06-18 22 views
6

Có mã sau đây, tại sao nhiệm vụ đầu tiên không gọi mẫu operator= trong Foo, nhưng thứ hai là gì? Điều gì sẽ xảy ra ở đây? Có trình biên dịch tạo ra cho việc chuyển nhượng đầu tiên ngay cả khi người dùng xác định mẫu tồn tại?sử dụng toán tử gán mẫu

#include <iostream> 

using namespace std; 

struct UberFoo { }; 

struct Foo : public UberFoo 
{ 
    template<typename T> void operator=(const T& v) { cout << "const T&" << endl; Set(v); } 
    template<typename T> void operator=(T& v) { cout << "T&" << endl; return Set(v); } 

    virtual void Set(const Foo&) { cout << "Foo::Set(const Foo&)" << endl; } 
    virtual void Set(const UberFoo&) { cout << "Foo::Set(const UberFoo&)" << endl; } 
}; 

struct Bar : public Foo 
{ 
    virtual void Set(const Foo&) { cout << "Bar::Set(const Foo&)" << endl; } 
    virtual void Set(const UberFoo&) { cout << "Bar::Set(const UberFoo&)" << endl; } 
}; 

int main() 
{ 
    Bar a, b; 

    Foo & pa = a; 
    const Foo& rb = b; 
    const UberFoo & urb = b; 

    cout << "First" << endl; 

    pa = rb; 

    cout << endl << "Second" << endl; 

    pa = urb; 

    return 0; 
} 
+0

Tôi biết toán tử gán phải trả về Tự (để tạo chuỗi có thể) nhưng đây không phải là điểm – relaxxx

+0

Có, toán tử gán bản sao luôn được xác định trừ khi xóa một cách rõ ràng. Nếu bạn không xác định một, trình biên dịch sẽ. –

Trả lời

5

Trình biên dịch vẫn tạo ra một phi templatedoperator= mà việc chuyển nhượng đầu tiên liên kết với. Trong bài tập thứ hai, operator= là một ứng cử viên tốt hơn (vì nó không liên quan đến diễn viên), do đó người ta được chọn.

Bạn có thể thấy rằng bằng cách thêm dòng sau trong mã của bạn:

Foo& operator=(const Foo&) = delete; 

Hoặc buộc mẫu thích hợp gọi:

pa.operator=<Foo>(b); 

The standard nói (tôi nhấn mạnh):

12,8 Sao chép và di chuyển đối tượng lớp học, §12.8/17, trang 271:

Một người dùng tuyên bố tử gán bản sao X :: operator = là một tổ chức phi tĩnh phi mẫu hàm thành viên của lớp X với chính xác một tham số kiểu X, X &, const X &, dễ bay hơi X & hay const dễ bay hơi X &

§12.8/18, cùng page:

Nếu định nghĩa lớp không tuyên bố một cách rõ ràng một toán tử gán sao chép, một tuyên bố hoàn toàn.

+0

và chữ ký được tạo ra trông như thế nào? khi 'T' được suy ra dưới dạng' Foo', chữ ký giống nhau: 'void operator = (const Foo &)' do đó điều này tốt bằng trình biên dịch tạo ra. Tại sao 'T' không được suy luận là' Foo'? – relaxxx

+0

@relaxxx: Nó có thể là "tốt" là toán tử gán bản sao, nhưng nó là _not_ toán tử gán bản sao, theo các quy tắc được trích dẫn trong câu trả lời này. 'T' có thể được suy ra dưới dạng' Foo' nhưng không cần điều đó xảy ra, vì đã có một hàm không phải mẫu phù hợp để gọi và được ưu tiên. –

+0

@relaxxx Như LRIO đã nói, chữ ký giống nhau, nhưng chức năng được tô điểm. Mẫu không phải là mẫu phù hợp hơn mẫu trong quá trình phân giải quá tải, do đó toán tử đã xóa được chọn là ứng viên tốt nhất. –

1

lý do tại sao nhiệm vụ đầu tiên không gọi nhà điều hành mẫu = trong Foo, nhưng giây? Điều gì sẽ xảy ra ở đây?

Ngoài thực tế được giải thích bởi câu trả lời của William về hàm toán tử gán bản sao được tạo ngầm, quá trình phân giải quá tải cũng có cách chơi trong này. Dưới đây là các ứng cử viên cho toán tử gán đầu tiên, sau khi thay thế đối số mẫu, như được trình biên dịch thấy:

Foo& operator=(const Foo&);  // implicitly generated 
void operator=(const Foo&);  // template generated 

Mọi thứ đều bằng nhau, các hàm nontemplate được ưu tiên hơn các mẫu chức năng. Theo C++ 11 (dự thảo N3337), 13.3.3/1 (tôi nhấn mạnh)

Với những định nghĩa, một chức năng F1 khả thi được xác định là một chức năng tốt hơn so với một chức năng F2 khả thi nếu cho tất cả các đối số i, ICSi (F1) không phải là chuỗi chuyển đổi tồi tệ hơn ICSi (F2), và sau đó

- đối với một số đối số j, ICSj (F1) là chuỗi chuyển đổi tốt hơn ICSj (F2), hoặc, nếu không phải vậy,

- ngữ cảnh là khởi tạo bởi chuyển đổi do người dùng xác định (xem 8.5, 13.3.1.5, và 13.3.1.6) và trình tự chuyển đổi chuẩn từ kiểu trả về của F1 thành kiểu đích (nghĩa là loại thực thể được khởi tạo) là chuỗi chuyển đổi tốt hơn chuỗi chuyển đổi chuẩn từ kiểu trả về của F2 cho loại đích. [...] hoặc, nếu không muốn nói rằng,

- F1 là một hàm phi mẫu và F2 là một hàm mẫu chuyên môn, hoặc, nếu không muốn nói rằng,

- F1 và F2 là hàm mẫu các chuyên ngành và mẫu chức năng cho F1 là chuyên biệt hơn so với mẫu cho F2 theo các quy tắc đặt hàng một phần được mô tả trong 14.5.6.2.

Điều này giải thích tại sao quá tải không phải mẫu được chọn. Bạn có thể xác minh cùng với một bài tập nhỏ:

void print(int, int) { std::cout << "int"; } 
template <typename T> void print(T, T) { std::cout << "T"; } 
print(1, 2); // prints int 
print<>(1, 2); // prints T 

Đối với các nhiệm vụ thứ hai, nó thấy

Foo& operator=(const Foo&);  // implicitly generated 
void operator=(const UberFoo&); // template generated 

Đây là mẫu được tạo ra chức năng là một trận đấu chặt chẽ hơn so với mặc nhiên tạo ra một và do đó nó là đã chọn.

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