Reputation: 179
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
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