c++pointersc++11smart-pointers

invalid user-defined conversion from Pointer<Node> to Node*


#include <cstddef>
#include <stdexcept>
#include <cassert>

template <typename T>
class Pointer {
private:
    std::ptrdiff_t offset;

    std::ptrdiff_t calculateOffset(const T* ptr) const {
        if (ptr == nullptr) return 0;
        return (char*)ptr - (char*)this;
    }


public:
    Pointer() : offset(0) {}

    Pointer(T* p) : offset(calculateOffset(p)) {}

    Pointer(const Pointer<T>& other) :
        offset(calculateOffset(other)) {}

    Pointer(Pointer<T>&& other) noexcept : offset(other.offset) {
        other.offset = 0;
    }

    ~Pointer() {
            offset = 0;
    }

    operator T* const() {
        assert(offset != 0);
        T* temp = (T*) ((char*)this + offset);
        return temp;
    }

    Pointer<T>& operator=(Pointer<T> other)  {
        if (this != &other) {
            T* temp = other;
            offset = calculateOffset(temp);
        }
        return *this;
    }

    T* operator->() const {
        T* ptr = *this;
        assert(ptr != nullptr);
        return ptr;
    }
};

I am trying to create a seamless Pointer wrapper which can be used to replace raw pointers in my project. The problem I am facing is that I can't declare explicit getter function to get the pointer value because it will lead to lot of changes in the codebase. I tried creating this class with type conversion operator so that Pointer<T> can be converted to T* seamlessly. I am now facing the error:

Pointer1.h:24:31: error: invalid user-defined conversion from 'const Pointer<Node>' to 'const Node*' [-fpermissive]
   24 |         offset(calculateOffset(other)) {}
      |                ~~~~~~~~~~~~~~~^~~~~~~

I scrapped through other questions and saw the discussion related to most vexing parse but I couldn't gather what I am doing wrong.


Solution

  • The operator T* const() is a member function without const-qualifiers, whose return type is T* const. So the this pointer of this member function has type Pointer<T>*. However, in the copy constructor Pointer(const Pointer<T>& other), other is a const-qualified reference to Pointer<T>.

    You cannot call a non-const member function through a const-qualified reference, since you cannot use it initialize the this pointer using the const-qualified reference other without const_cast.

    So you should add a const qualifier to the function:

        operator T* () const {
            assert(offset != 0);
            T* temp = (T*) ((char*)this + offset);
            return temp;
        }