Laine
Laine

Reputation: 179

How to write c++ smartpointer for operator == / !=

In my project, I have some mechanism in my framework, so I have designed C++ smart-pointer myself.

But I encounter equality and inequality problem for smart-pointer.

class Ref {
public:
    void ref(){}
    void unref(){}
};

template<class T>
class SmartPtr
{
public:
    typedef T element_type;

    SmartPtr() : _ptr(nullptr) {}
    SmartPtr(T* ptr) : _ptr(ptr) { if (_ptr) _ptr->ref(); }
    SmartPtr(const SmartPtr& rp) : _ptr(rp._ptr)  { if (_ptr) _ptr->ref(); }

    template<class Other>
    SmartPtr(const SmartPtr<Other>& rp) : _ptr(rp._ptr)
    { if (_ptr) _ptr->ref(); }

    ~SmartPtr() { if (_ptr) _ptr->unref();  _ptr = 0; }

    SmartPtr& operator = (const SmartPtr& rp)
    {    assign(rp);    return *this;}

    template<class Other> SmartPtr& operator = (const SmartPtr<Other>& rp)
    {   assign(rp);    return *this;}
    template<class Other> void assign(const SmartPtr<Other>& rp) 
    {_ptr=rp._ptr;}

    operator T*() const { return _ptr; }

    template<class U>
    bool operator == (const SmartPtr<U>& rp) const
    { return (_ptr==rp._ptr); }

    template<class U>
    friend bool operator == (const U* ptr, const SmartPtr& rp)
    { return (ptr==rp._ptr); }

    template<class U>
    friend bool operator == (const SmartPtr& rp, const U* ptr)
    { return (ptr==rp._ptr); }

private:
    template<class U> friend class SmartPtr;
    T* _ptr;
};

The code failed when I writing following code :

class A : public Ref {};
class B : public A {};
SmartPtr<A> a1 = new A;
A* a2 = a1;
bool flag = a1==a2;  // ambiguous error, error message follows

SmartPtr<B> b = new B;
SmartPtr<A> a3 = b;
bool flag2 = a3==b;  // build pass

compilation error message

maybe "bool operator ==<A>(const U *,const ECB::SmartPtr<A> &)"
or    "bool operator ==<A>(const U *,const ECB::SmartPtr<B> &)"
or    "built-in C++ operator==(T1, T1)"
or    "built-in C++ operator==(A *SmartPtr<A>::* , A *SmartPtr<A>::* )"

How to modify the template SmartPtr class to avoid the ambiguous errors ?

Upvotes: 3

Views: 231

Answers (1)

Artyer
Artyer

Reputation: 40891

Compiling with gcc, you get a clearer error message:

warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
       bool flag = a1==a2;
                       ^~
note: candidate 1: 'bool operator==(const SmartPtr<T>&, const U*) [with U = A; T = A]'
    friend bool operator == (const SmartPtr& rp, const U* ptr)
                ^~~~~~~~
note: candidate 2: 'operator==(A*, A*)' <built-in>

There are two candidates. Your declaration of:

template<class U>
friend bool operator == (const SmartPtr& rp, const U* ptr);

And the built in one that would be called by a1.operator A*() == a2 (Comparing two pointers).

To use the first one, U would be deduced as A, but the parameter is still const A*, whilst your pointer is A*. In the second one, your smart pointer will have to be converted to a regular pointer using the user-defined conversion (operator T*()).

In both cases, there is a conversion (Even though the user defined conversion is usually worse than A* to const A* conversion).

The problem is, when considering the first argument (the smart pointer), your friend operator is the better match. But for the second argument, the built-in one is the better match. GCC overcomes this by using the logic that "user-defined conversion operators is a worse conversion than A* to const A*", but this is a non-standard extension (And goes away with -pedantic).

A fix is to have a non-const overload too:

template<class U>
friend bool operator == (const SmartPtr& rp, U* ptr)
{ return (ptr==rp._ptr); }

(And you can remove the const overload, since U can be deduced as const A in that case)

Upvotes: 3

Related Questions