如何为运算符 == / != 编写 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
)
在我的项目中,我的框架中有一些机制,所以我自己设计了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
)