如何为运算符 == / != 编写 C++ 智能指针

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

在我的项目中,我的框架中有一些机制,所以我自己设计了C++智能指针。

但是我遇到了智能指针的相等和不等问题。

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;
};

当我编写以下代码时代码失败:

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

编译错误信息

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>::* )"

如何修改模板SmartPtr class来避免模棱两可的错误?

用gcc编译,报错信息更清晰:

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>

有两位候选人。您的声明:

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

和内置的一个将被 a1.operator A*() == a2 调用(比较两个指针)。

要使用第一个,U 将推导为 A,但参数仍然是 const A*,而您的指针是 A*。在第二个中,您的智能指针必须使用用户定义的转换 (operator T*()) 转换为常规指针。

在这两种情况下,都有一个转换(尽管用户定义的转换通常比 A*const A* 的转换更差)。

问题是,在考虑第一个参数(智能指针)时,您的友元运算符更匹配。但是对于第二个参数,内置的是更好的匹配。 GCC 通过使用 "user-defined conversion operators is a worse conversion than A* to const A*" 的逻辑克服了这个问题,但这是一个非标准扩展(并且与 -pedantic 一起消失)。

解决方法是也有一个非常量重载:

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

(并且您可以删除 const 重载,因为在这种情况下 U 可以推导为 const A