2011-02-04 44 views
17

Dưới đây là ví dụ mã của tôi:quá tải toán tử ->

class X 
{ 
public: 
     void f() {} 
}; 

class Y : public X 
{ 
public: 
     X& operator->() { return *this; } 
     void f() {} 
}; 

int main() 
{ 
     Y t; 
     t.operator->().f(); // OK 
     t->f(); // error C2819: type 'X' does not have an overloaded member 'operator ->' 
       // error C2232: '->Y::f' : left operand has 'class' type, use '.' 
} 

Tại sao trình biên dịch đang cố gắng để "di chuyển trách nhiệm" đối với nhà khai thác> từ Y đến X? Khi tôi thực hiện X :: op-> sau đó tôi không thể trả về X ở đó - lỗi biên dịch nói "đệ quy vô hạn" trong khi trả về một số Z từ X :: op-> một lần nữa nói rằng Z không có toán tử->, do đó sẽ cao hơn và cao hơn trong phân cấp.

Có ai có thể giải thích hành vi thú vị này không? :)

Trả lời

18

Vấn đề là operator -> có nghĩa vụ phải trả về một con trỏ , không phải là một tài liệu tham khảo . Ý tưởng là operator -> nên trả về một con trỏ đến đối tượng thực mà phải có con trỏ được áp dụng cho nó. Ví dụ, đối với một lớp học với một quá tải operator ->, mã

myClass->myValue; 

chuyển thành

(myClass.operator->())->myValue; 

Vấn đề với mã của bạn là operator -> trả về một tham chiếu, vì vậy văn bản

myClass.operator->().f(); 

hoàn toàn hợp pháp vì bạn đang gọi một cách rõ ràng toán tử, nhưng viết

myClass->f(); 

là bất hợp pháp, bởi vì trình biên dịch đang cố gắng để mở rộng nó để

myClass.operator->()->f(); 

và kiểu trả về của operator-> không phải là một con trỏ.

Để khắc phục điều này, hãy thay đổi mã của bạn để bạn trả về con trỏ trong operator ->. Nếu bạn muốn quá tải một toán tử để trả về một tham chiếu, hãy quá tải operator *; con trỏ dereferences thực sự nên sản xuất tài liệu tham khảo.

+9

Tôi sẽ không nói nó giả sử để trả về một con trỏ, chỉ là bất cứ điều gì nó sẽ trả về nhu cầu để hỗ trợ 'nhà khai thác> '. – GManNickG

+1

@ GMan- Điểm tốt. Tôi đã đi cho sự đơn giản ở đây, nhưng bạn là chính xác. Có một số thủ thuật thực sự thú vị mà bạn có thể kéo ra bằng con trỏ thông minh dựa vào kỹ thuật này. – templatetypedef

+0

@GMan: Vì các kiểu như vậy được gọi chung là * con trỏ thông minh *, tôi không nghĩ templatetypedef là sai khi sử dụng thuật ngữ * con trỏ *, anh ấy chỉ sử dụng nó theo nghĩa chung. –

2

Cú pháp là sai, nên là:

T> T2

T2* T::operator ->();​ 

Nhìn vào bài viết wikipedia của: Operators in C and C++

Nếu bạn muốn quá tải, bạn phải sử dụng cú pháp đúng cho toán tử quá tải

19

Bởi vì đó là cách vượt trội aded -> hoạt động trong C++.

Khi bạn sử dụng quá tải ->, biểu thức a->b được dịch sang a.operator->()->b. Điều này có nghĩa là toán tử quá tải của bạn -> phải trả về một cái gì đó mà chính nó sẽ hỗ trợ một ứng dụng khác của toán tử ->. Vì lý do này, một yêu cầu duy nhất của quá tải -> có thể biến thành một chuỗi dài các yêu cầu quá tải -> s cho đến khi nó cuối cùng đạt đến một ứng dụng được xây dựng trong ->, kết thúc chuỗi.

Trong trường hợp của bạn, bạn cần trả lại từ quá tải ->, không phải X&.

1

Bạn có thể muốn:

class Y : public X 
{ 
public: 
     X* operator->() { return this; } 
     void f() {} 
};