7

my problem is a simple one. I have a class template that holds a pointer to a dynamically allocated type. I want to overload the indirection operator so that referring to the class template instance with the -> operator I get redirected as if I use the pointer contained within directly.

template<class T>
class MyClass
 {
  T *ptr;
  ...
  // created dynamic resource for ptr in the constructor
 };

Create myclass of some type:

MyClass<SomeObject> instance;

So what I want is instead of having to type:

instance.ptr->someMemberMethod();

I simply type:

intance->someMemberMethod();

Even thou instance is not a pointer it behaves as if it is the pointer instance contains. How to bridge that gap by overloading the operator?

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Modern C++ Design (Andrei Alexandrescu) has some really good info on the subject if you would like more depth. – Tom Kerr Jan 12 '12 at 19:25

3 Answers3

13

You can just overload operator-> and operator*:

template<class T>
class MyClass
{
    T* ptr;

public:
    T* operator->() {
        return ptr;
    }

    // const version, returns a pointer-to-const instead of just a pointer to
    // enforce the idea of the logical constness of this object 
    const T* operator->() const {
        return ptr;
    }

    T& operator*() {
        return *ptr;
    }

    // const version, returns a const reference instead of just a reference
    // to enforce the idea of the logical constness of this object
    const T& operator*() const {
        return *ptr;
    }
};

Note that, due to a design decision by the creator of the language, you can't overload the . operator.

Also, you might think that operator* would overload the multiplication operator instead of the dereference operator. However, this is not the case, because the multiplication operator takes a single argument (while the dereference operator takes no arguments), and because of this, the compiler can tell which one is which.

Finally, note that operator-> returns a pointer but operator* returns a reference. It's easy to accidentally confuse them.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Nice answer. But can you tell the difference between const-version and non-const version ? When would I use either of them ? – SimpleGuy Aug 18 '16 at 03:19
  • Also why it is needed to overload `->` and `*`. Why is just overloading `->` not enough ? – SimpleGuy Aug 18 '16 at 03:40
  • 1
    @SimpleGuy Users of your class generally expect `foo->bar` to be equivalent to `(*foo).bar`. Having them different will shock many people. – Bernard Jun 26 '17 at 12:11
  • @Bernard Oh.. from that perspective it is good to do so.. – SimpleGuy Jun 27 '17 at 07:24
5

Overload the -> operator:

template <typename T> class MyClass
{
    T * p;
public:
    T       * operator->()       { return p; }  // #1
    T const * operator->() const { return p; }
};

Note that both overloads don't mutate the object; nonetheless we decide to make #1 non-const, so that we bequeath constness of the object onto the pointee. This is sometimes called "deep constness propagation" or something of this sort. The language D takes this much further.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • "we decide to make #1 non-const" -- at least, we can if we want to. Standard smart pointers don't, essentially for the same reason that it's possible to modify an object of type `T` via a `T *const`. Which you do depends whether `instance` is "logically" an indirect reference to the other object (in which case imitate standard smart pointers) or not (in which case curse Bjarne that you can't overload `operator.`). – Steve Jessop Jan 12 '12 at 19:36
  • 1
    @SteveJessop: "We" as in "you and I, on this journey into C++", rather than "we the extant rulers of all opinion", I suppose. Sorry, old habits :-) – Kerrek SB Jan 12 '12 at 19:38
3

The member access operator can be overloaded to return a pointer to the object to be accessed:

T * operator->() {return ptr;}
T const * operator->() const {return ptr;}

You might also want the deference operator, to make it feel even more like a pointer; this returns a reference instead:

T & operator*() {return *ptr;}
T const & operator*() const {return *ptr;}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644