0

I am trying to write my own implementation of unique_ptr in C++, and here is my UniquePtr header file:

#include <iostream>

template <typename T>
class UniquePtr
{
    T* ptr;

public:
    UniquePtr(T* _ptr);

    UniquePtr(const UniquePtr& other) = delete;
    UniquePtr& operator=(const UniquePtr& other) = delete;

    UniquePtr(UniquePtr&& dying) noexcept;
    UniquePtr& operator=(UniquePtr&& dying) noexcept;

    T& operator*();
    const T& operator*() const;

    /*??? operator->() 
    {

    }*/

    /*??? operator->() const
    {

    }*/

    ~UniquePtr();
};

I am confused about what operator-> should return. I know that a call in the form uniquePtr->member is translated to (*uniquePtr).member, but this still does not help me understand what the operator-> should return.

I understand that operators are basically functions with a special name, so we can think of -> as a function that takes just one argument - a pointer to the object (which all member functions get). However, I am a little confused about how the translation of the call to uniquePtr->member should dictate the way in which we should override it in the class (what its return type should be).

Could someone explain how to implement operator-> for this custom UniquePtr class and why it is implemented that way?

This does not answer my question:

What are the basic rules and idioms for operator overloading?

3
  • The duplicate DOES answer your question. If you search for operator-> in that page you'll find a short paragraph that explains how overloading operator-> works. It even uses an example of pointer-like type which says you should return value_type*, just the same as your case. Commented Jun 29 at 3:09
  • @WeijunZhou, stop being so over yourself! Even the user who answered my question said that the provided one which supposedly answers it doesn't explain the operator-> that good. Knowing that I can find an answer to MY question, even though I said I could not. Stay in your lane, please. Commented Jun 29 at 10:45
  • I agree the tone is not ideal and I apologize if if makes you feel offended. I should have commented like this: "I believe the suggested dupe and the link there answers your question, can you edit your question to explain why it doesn't instead of just stating that it doesn't? This may help get your question reopened." By the way, I didn't vote to close your question, by the way. Commented Jun 29 at 12:17

1 Answer 1

2

I know that a call in the form uniquePtr->member is translated to (*uniquePtr).member

That's the behavior of the built-in -> operator. If an overload is selected instead the compiler will transform

uniquePtr->member

to

(uniquePtr.operator->())->member

and this will be done recursively for the newly introduced -> operator.

You want this to result in (uniquePtr.ptr)->member, which after expansion of the built-in -> is (*(uniquePtr.ptr)).member. So you want your operator->() to simply return ptr. The return type should match the const-qualification of the overload.

Also note that the behavior of -> when selecting an operator overload that I explained above is special to -> and different than it is for any other binary operator. It is not something you can derive from a general rule about binary operator overloads.

4
  • Thank you for the response! I am still a little confused though. Why do the compiler translates uniquePtr->member to (uniquePtr.operator->())->member? I mean from one operator -> we get two operators ->. Does this make sense? Commented Jun 28 at 22:58
  • Sorry, but I don't believe my question is a duplicate, as the provided 'duplicate' question DOES NOT answer mine. Commented Jun 28 at 23:05
  • @user25687822 I did not close your question. That was another user. However, that linked question is considered a canonical duplicate for all questions on basic operator overloading explanations, even if the explanations for -> in the answers don't seem very good. Commented Jun 28 at 23:10
  • 1
    @user25687822 The compiler translates it that way, because that's the behaviour the standard requires. uniquePtr->member is first translated into (an equivalent of) (uniquePtr.operator->())->member. If the return type of the operator->() is an object that also has an operator->(), the expression is translated a second time into (uniquePtr.operator->().operator->())->member. That translating into calls of operator->() repeats until one of the operator->() returns a raw pointer (of type SomeType *). [which is essentially what user17732522 said].
    – Peter
    Commented Jun 29 at 1:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.