54

Đọc một số câu hỏi ở đây về SO về các toán tử chuyển đổi và các hàm tạo cho tôi suy nghĩ về sự tương tác giữa chúng, cụ thể là khi có một cuộc gọi 'không rõ ràng'. Hãy xem xét mã sau:Chuyển đổi constructor so với toán tử chuyển đổi: ưu tiên

class A; 

class B { 
     public: 
     B(){} 

     B(const A&) //conversion constructor 
     { 
       cout << "called B's conversion constructor" << endl; 
     } 
}; 

class A { 
     public: 
     operator B() //conversion operator 
     { 
       cout << "called A's conversion operator" << endl; 
       return B(); 
     } 
}; 

int main() 
{ 
    B b = A(); //what should be called here? apparently, A::operator B() 
    return 0; 
} 

Mã trên hiển thị "được gọi là toán tử chuyển đổi A", có nghĩa là toán tử chuyển đổi được gọi ngược với hàm tạo. Nếu bạn xóa/nhận xét mã operator B() từ A, trình biên dịch sẽ chuyển sang sử dụng hàm tạo thay thế (không có thay đổi nào khác đối với mã).

Câu hỏi của tôi là:

  1. Kể từ khi trình biên dịch không xem xét B b = A(); được một cuộc gọi mơ hồ, thì phải có một số loại được ưu tiên tại nơi làm việc ở đây. Chính xác ưu tiên này được thiết lập ở đâu? (một tham chiếu/trích dẫn từ tiêu chuẩn C++ sẽ được đánh giá cao)
  2. Từ quan điểm triết học hướng đối tượng, đây có phải là cách mã nên hoạt động không? Ai biết thêm về cách đối tượng A phải trở thành đối tượng B, A hoặc B? Theo C++, câu trả lời là A - có điều gì trong thực hành hướng đối tượng cho thấy điều này có đúng không? Đối với cá nhân tôi, nó sẽ có ý nghĩa một trong hai cách, vì vậy tôi quan tâm để biết làm thế nào sự lựa chọn đã được thực hiện.

Cảm ơn trước

+0

Dòng mà bạn nhận xét "// constructor" không phải là một nhà xây dựng bản sao. –

+0

Bạn nói đúng, tôi đã lạm dụng thuật ngữ này. Tôi đã chỉnh sửa nó. – GRB

Trả lời

42

Bạn làm sao chép khởi, và các chức năng ứng cử viên được coi là để làm các chuyển đổi trong trình tự chuyển đổi là chức năng chuyển đổi và nhà thầu chuyển đổi. Đây là trong trường hợp của bạn

B(const A&) 
operator B() 

Bây giờ, đó là cách bạn khai báo chúng. Quá tải phân giải tóm tắt ra khỏi đó, và biến đổi mỗi ứng cử viên vào một danh sách các tham số tương ứng với các đối số của cuộc gọi. Các thông số là

B(const A&) 
B(A&) 

Điều thứ hai là do hàm chuyển đổi là hàm thành viên. Các A& là cái gọi là tham số đối tượng tiềm ẩn được tạo ra khi một ứng viên là một hàm thành viên. Bây giờ, đối số có loại A. Khi ràng buộc tham số đối tượng ẩn, tham chiếu không const có thể liên kết với một giá trị. Vì vậy, một quy tắc khác nói rằng khi bạn có hai hàm khả thi có tham số là tham chiếu, thì ứng cử viên có chứng chỉ const ít nhất sẽ giành chiến thắng. Đó là lý do tại sao chức năng chuyển đổi của bạn thắng. Hãy thử làm cho operator B một hàm thành viên const. Bạn sẽ nhận thấy một sự mơ hồ.

Từ quan điểm triết học hướng đối tượng, đây có phải là cách mã nên hoạt động không? Ai biết nhiều hơn về một đối tượng A nên trở thành đối tượng B, A hoặc B như thế nào? Theo C++, câu trả lời là A - có điều gì trong thực hành hướng đối tượng cho thấy điều này có đúng không? Đối với cá nhân tôi, nó sẽ có ý nghĩa một trong hai cách, vì vậy tôi quan tâm để biết làm thế nào sự lựa chọn đã được thực hiện.

Để ghi lại, nếu bạn thực hiện chức năng chuyển đổi thành một hàm thành viên, thì GCC sẽ chọn hàm tạo (vì vậy GCC có vẻ nghĩ rằng B có nhiều doanh nghiệp hơn?). Chuyển sang chế độ pedantic (-pedantic) để làm cho nó gây ra một chẩn đoán.


Standardese, 8.5/14

Nếu không (ví dụ, đối với các trường hợp sao chép khởi còn lại), trình tự chuyển đổi người dùng định nghĩa có thể chuyển đổi từ các loại nguồn để loại đích hoặc (khi chuyển đổi chức năng được sử dụng) cho một lớp dẫn xuất của chúng được liệt kê như mô tả trong 13.3.1.4, và tốt nhất được chọn thông qua độ phân giải quá tải (13.3).

13.3.1.4

độ phân giải quá tải được sử dụng để chọn chuyển đổi người dùng định nghĩa được gọi. Giả sử rằng "CV1 T" là kiểu của đối tượng được khởi tạo, với T một kiểu lớp, các chức năng ứng cử viên được lựa chọn như sau:

  • Các nhà xây dựng chuyển đổi (12.3.1) của T là chức năng ứng cử viên.
  • Khi loại biểu thức khởi tạo là loại lớp "cv S", các hàm chuyển đổi của S và các lớp cơ sở của nó được xem xét. Những người không được ẩn trong S và mang lại một loại có phiên bản cv-unqualified cùng loại với T hoặc là một lớp dẫn xuất của chúng là các hàm ứng cử viên. Các hàm chuyển đổi trả về "tham chiếu đến X" trả về các giá trị của loại X và do đó được coi là mang lại X cho quá trình chọn các hàm ứng cử viên này.

Trong cả hai trường hợp, danh sách đối số có một đối số, là biểu thức khởi tạo. [Lưu ý: đối số này sẽ được so sánh với tham số đầu tiên của các hàm tạo và chống lại tham số đối tượng ẩn của các hàm chuyển đổi. ]

13.3.3.2/3

  • Chuẩn chuỗi chuyển đổi S1 là một chuỗi chuyển đổi tốt hơn so với trình tự chuyển đổi tiêu chuẩn S2 nếu [...] S1 và S2 là các ràng buộc tham chiếu (8.5.3), và các loại tham chiếu tham chiếu là cùng loại ngoại trừ các loại vòng loại cv cấp cao nhất và loại mà tham chiếu được khởi tạo bằng tham chiếu S2 là cv đủ điều kiện hơn loại tham chiếu được khởi tạo bởi S1 đề cập.
+1

Ah, vì vậy vấn đề của tôi không phải là ưu tiên giữa hàm tạo và toán tử, mà là 'const'-ness của mỗi hàm. Bạn đã đúng, thay đổi 'toán tử B()' thành 'toán tử B() const' dẫn đến một lỗi mơ hồ. – GRB

+1

Thật trùng hợp khi tôi đưa cùng một "phân tích" vào một câu trả lời khác của tôi như một ví dụ trước đó: http://stackoverflow.com/questions/1051379/is-there-a-difference-in-c-between-copy- initialization-and-assignment-initializ/1051468 # 1051468 xD –

+0

Câu hỏi nhanh về câu đầu tiên của câu trả lời - 'bạn khởi tạo sao chép ...'. Tôi không thấy một bản sao xuất hiện ở đâu cả - tức là, không có 'A' nào được xây dựng từ một' A' khác (cũng không phải 'B' từ một' B' khác) - vì vậy tôi đúng là bạn không có nghĩa là 'copy initialization', nhưng thay vì một cái gì đó dọc theo dòng 'initializationization'? Cảm ơn! –

3

Dường MSVS2008 có ý kiến ​​riêng của mình về việc lựa chọn xây dựng: nó gọi copy constructor trong B bất kể constness của nhà điều hành của một. Vì vậy, hãy cẩn thận ở đây ngay cả khi tiêu chuẩn chỉ định hành vi chính xác.

Tôi nghĩ MSVS chỉ tìm kiếm hàm tạo phù hợp trước toán tử chuyển đổi, nhưng sau đó thấy rằng nó bắt đầu gọi toán tử A() nếu bạn loại bỏ từ const khỏi hàm tạo B. Có lẽ nó có một số hành vi đặc biệt cho thời gian, bởi vì đoạn mã sau vẫn gọi hàm tạo của B:

A a; 

B b = a; 
Các vấn đề liên quan