How to create an operator-> for a class unique_ptr

Viewed 86

I have to implement a simple "unique_ptr" class supporting only a constructor, destructor, –>, *, and release(). And I did below.

However, it feels weird to write "up.operator->()" to get the pointer p. I would be more logical to write "up->p". But how do I do that? Thanks!

#include <iostream>
#include <stdexcept>

template <class T>
class unique_ptr
{
    T *p;

public:        
    unique_ptr(T *ptr)
        : p{ptr}
    {
    }
    ~unique_ptr() { delete p; }


    T *operator->() const { return p; } // returns a pointer
    T operator*() const { return *p; }
    T *release()
    {
        T *ptr = p;
        p = nullptr;
        return ptr;
    }
};

template <class T>
void print(const unique_ptr<T> &up, const std::string &s)
{
    std::cout << s << " up.operator->(): " << up.operator->() << '\n';
    std::cout << s << " up.operator*(): " << up.operator*() << '\n'; 
}

int main()
try
{
    int *ptr = new int(10);

    unique_ptr<int> up(ptr);
    print(up, "up: ");

}

catch (std::exception &e)
{
    std::cerr << "exception: " << e.what() << '\n';
    return 1;
}
catch (...)
{
    std::cerr << "exception\n";
    return 2;
}
2 Answers

However, it feels weird to write "up.operator->()" to get the pointer p.

It feels weird because the member access operator is not generally used to get a pointer to the object (although you can do it using the operator->() syntax, as you demonstrated). Member access operator is used to access members of the object. In your example, you have a unique pointer of int. int doesn't have a member, so it doesn't make sense to use the member access operator.

Here is an example of how to use it:

struct S {
    int member;
};

unique_ptr<S> up(new S{10});
int value_of_member = up->member;

would be more logical to write "up->p"

That wouldn't be logical unless p is a member of the pointed object.

How to create an operator-> for a class unique_ptr

Like you did in the example. As far as I can tell, there was no problem with how you create the operator, but rather how to use it.


P.S. Your unique pointer is copyable, movable and assignable, but those operations are horribly broken leading to undefined behaviour. See rule of 5.

As others have noted in comments this implementation of a single ownership smart pointer is incomplete and the operator*() is incorrect in that it doesn't return a reference and thus does not facilitate making assignments through the pointer.

However to answer the question,

it feels weird to write "up.operator->()" to get the pointer p. I would be more logical to write "up->p". But how do I do that?

Well you wouldnt want to do that as p is part of the private implementation of your smart pointer class. It is weird to write up.operator->() because that is not how the -> is typically used. It is typically used as shorthand to access the members of a struct or class that is slightly less verbose than the * operator in combination with member access via .. To use your pointer then in a less weird way you need the template parameter to be instantiated with some type that has fields, e.g.

struct foo {
    int bar;
};

void print(const unique_ptr<foo>& up, const std::string& s)
{
    std::cout << s << " up.operator->(): " << up->bar << '\n';
    std::cout << s << " up.operator*(): " << (*up).bar << '\n';
}

int main()
{
    unique_ptr<foo> up(new foo{ 42 });
    print(up, "up: ");

}
Related