[ Team LiB ] |
![]() ![]() |
Gotcha #24: Operator -> SubtletiesThe predefined -> operator is binary: the left operand is a pointer, and the right operand is the name of a class member. Overloaded operator -> is a unary member function!
class Ptr { public: Ptr( T *init ); T *operator ->(); // . . . private: T *tp_; }; An invocation of overloaded -> must return something that may then be used with an -> operator to access a member:
Ptr p( new T ); p->f(); // p.operator ->()->f()! One way to look at it is that the -> token is not "consumed" by an overloaded operator -> but is instead retained for eventual use as the predefined -> operator. Typically, some additional semantics are attached to the overloaded -> to create a "smart pointer" type:
T *Ptr::operator ->() { if( today() == TUESDAY ) abort(); else return tp_; } We mentioned that an overloaded operator -> must return "something" that may be then used to access a member. That something doesn't have to be a predefined pointer. It could be a class object that itself overloads operator ->:
class AugPtr { public: AugPtr( T *init ) : p_( init ) {} Ptr &operator ->(); // . . . private: Ptr p_; };
Ptr &AugPtr::operator ->() { if( today() == FRIDAY ) cout << '\a' << flush; return p_; } This allows the cascading of smart pointer responsibilities:
AugPtr ap( new T ); ap->f(); // ap.operator ->().operator ->()->f()! Note that the sequence of operator -> activations is always determined statically by the type of the object that contains the operator -> definition, and the sequence of operator -> member function calls always terminates in a call that returns a predefined pointer to class. For example, applying the -> operator to an AugPtr will always result in the sequence of calls AugPtr::operator -> followed by Ptr::operator ->, followed by a predefined -> operator applied to a T * pointer. (See Gotcha #83 for a more realistic example of the use of operator ->.) |
[ Team LiB ] |
![]() ![]() |