C++ 比较运算符中的共享指针常数==
我无意中发现了我正在使用的共享指针的意外行为 共享指针实现引用计数,并在必要时分离(例如制作副本)非常量使用中包含的实例。C++ 比较运算符中的共享指针常数==,c++,constants,shared-ptr,comparison-operators,C++,Constants,Shared Ptr,Comparison Operators,我无意中发现了我正在使用的共享指针的意外行为 共享指针实现引用计数,并在必要时分离(例如制作副本)非常量使用中包含的实例。 为此,对于每个getter函数,智能指针都有一个const和一个non-const版本,例如:运算符T*()和运算符T const*()const 问题:将指针值与nullptr进行比较会导致分离 预期:我认为比较运算符将始终调用const版本 简化示例: (此实现没有引用计数,但仍然显示问题) (因为我为什么要比较非常量指针?) 其中应调用: inline operato
为此,对于每个getter函数,智能指针都有一个
const
和一个non-const
版本,例如:运算符T*()
和运算符T const*()const
问题:将指针值与nullptr
进行比较会导致分离
预期:我认为比较运算符将始终调用const版本
简化示例:(此实现没有引用计数,但仍然显示问题) (因为我为什么要比较非常量指针?) 其中应调用:
inline operator const T *() const
其他问题:
- 为什么默认情况下不使用const运算符?
- 这是因为不能仅通过返回值的类型来选择函数吗?
=>这一点在中得到了回答
- 这是因为不能仅通过返回值的类型来选择函数吗?
- 为什么比较运算符的默认实现不将参数作为常量引用,然后调用常量函数 你能引用一个C++引用吗?< /LI>
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
这种理解是错误的。操作符的重载解析相当复杂,内置版本的操作符有一大组代理签名参与解析。标准中的[over.match.oper]和[over.build]中描述了此过程
具体而言,[over.build]第16页和第17页定义了相关的内置平等候选者。这些规则表示,对于每种指针类型T
,都存在一个操作符==(T,T)
。现在,int*
和const int*
都是指针类型,因此两个相关的签名是operator==(int*,int*)
和operator==(const int*,const int*)
。(还有操作符==(std::nullptr\u t,std::nullptr\u t)
,但它不会被选中。)
为了区分这两个重载,编译器必须比较转换序列。对于第一个参数,
nullptr\u t->int*
和nullptr\u t->const int*
都是相同的;它们是指针转换。将常量
添加到其中一个指针将被包括在内。(参见[conv.ptr])对于第二个参数,转换分别是SharedPointer->int*
和SharedPointer->const int*
。第一个是用户定义的转换,调用操作符int*()
,无需进一步转换。第二种是用户定义的转换,调用运算符const int*()const
,这需要首先进行限定转换,以便调用const
版本。因此,首选非常量版本。当常量和非常量上存在重载时,如果您使用的对象是非常量,编译器将始终调用非常量版本。否则,何时调用非常量版本
如果要显式使用常量版本,请通过常量引用调用它们:
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
这种理解是错误的。操作符的重载解析相当复杂,内置版本的操作符有一大组代理签名参与解析。标准中的[over.match.oper]和[over.build]中描述了此过程
具体而言,[over.build]第16页和第17页定义了相关的内置平等候选者。这些规则表示,对于每种指针类型T
,都存在一个操作符==(T,T)
。现在,int*
和const int*
都是指针类型,因此两个相关的签名是operator==(int*,int*)
和operator==(const int*,const int*)
。(还有操作符==(std::nullptr\u t,std::nullptr\u t)
,但它不会被选中。)
为了区分这两个重载,编译器必须比较转换序列。对于第一个参数,
nullptr\u t->int*
和nullptr\u t->const int*
都是相同的;它们是指针转换。将常量
添加到其中一个指针将被包括在内。(参见[conv.ptr])对于第二个参数,转换分别是SharedPointer->int*
和SharedPointer->const int*
。第一个是用户定义的转换,调用操作符int*()
,无需进一步转换。第二种是用户定义的转换,调用运算符const int*()const
,这需要首先进行限定转换,以便调用const
版本。因此,首选非常量版本。这是因为表达式testInst==nullptr
是如何解析的:
testInst
属于SharedPointer
类型nullptr
是(为了简化)类型T*
或void*
,具体取决于用例。因此表达式读取
SharedPointer==int*
int*==int*
这涉及到调用
操作符int*()
或操作符int const*()const
[需要引用]
SharedPointer==SharedPointer
这个
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
bool operator==(T const* a, T const* b)
class X {
public:
operator int * () { std::cout << "1\n"; return nullptr; }
operator const int * () { std::cout << "2\n"; return nullptr; }
operator int * () const { std::cout << "3\n"; return nullptr; }
operator const int * () const { std::cout << "4\n"; return nullptr; }
};
int main() {
X x;
const X & rcx = x;
int* pi1 = x;
const int* pi2 = x;
int* pi3 = rcx;
const int* pi4 = rcx;
}
1
2
3
4