比较运算符==中的共享指针常量

Mar*_*ngs 0 c++ const shared-ptr comparison-operators

我偶然发现了我正在使用的共享指针的意外行为.

共享指针实现引用计数和分离(例如,复制),如果需要,包含非const使用的实例.
为实现此目的,对于每个getter函数,智能指针都有一个const和一个non-const版本,例如:operator T *()operator T const *() const.

问题:比较指针值nullptr导致分离.

预期:我认为比较运算符总是会调用const版本.

简化示例:(
此实现没有引用计数,但仍显示问题)

#include <iostream>

template<typename T>
class SharedPointer
{
public:
    inline operator T *() { std::cout << "Detached"; return d; }
    inline operator const T *() const { std::cout << "Not detached"; return d; }
    inline T *data() { std::cout << "Detached"; return d; }
    inline const T *data() const { std::cout << "Not detached"; return d; }
    inline const T *constData() const { std::cout << "Not detached"; return d; }

    SharedPointer(T *_d) : d(_d) { }

private:
    T *d;
};


int main(int argc, char *argv[])
{
    SharedPointer<int> testInst(new int(0));

    bool eq;

    std::cout << "nullptr  == testInst: ";
    eq = nullptr == testInst;
    std::cout << std::endl;
    // Output: nullptr  == testInst: Detached

    std::cout << "nullptr  == testInst.data(): ";
    eq = nullptr == testInst.data();
    std::cout << std::endl;
    // Output: nullptr  == testInst.data(): Detached

    std::cout << "nullptr  == testInst.constData(): ";
    eq = nullptr == testInst.constData();
    std::cout << std::endl;
    // Output: nullptr  == testInst.constData(): Not detached
}
Run Code Online (Sandbox Code Playgroud)

问题1:为什么职能的非const版本调用时就应该足以调用const版本?

问题2:为什么可以调用非const版本?比较运算符(特别是与不可变的比较nullptr)是否总是在const引用上运行?


对于记录:
我正在使用的共享指针是Qt QSharedDataPointer持有一个QSharedData衍生实例,但这个问题不是Qt特定的.


编辑:

在我的理解中,nullptr == testInst会调用

bool operator==(T const* a, T const* b)
Run Code Online (Sandbox Code Playgroud)

(因为我为什么要比较非常量指针?)

应该调用:

inline operator const T *() const 
Run Code Online (Sandbox Code Playgroud)

进一步的问题:

所以这个问题归结为:

  • 为什么比较运算符的默认实现不将参数作为const引用,然后调用const函数?
  • 你能举一个c ++参考吗?

Seb*_*edl 5

当const和非const存在重载时,如果您使用的对象是非const,则编译器将始终调用非const版本.否则,什么时候会调用非const版本?

如果要显式使用const版本,请通过const引用调用它们:

const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;
Run Code Online (Sandbox Code Playgroud)

在Qt的上下文中QSharedDataPointer,您还可以在constData需要指针时显式使用该函数.

对于预期用途QSharedDataPointer,此行为通常不是问题.它应该是Facade类的成员,因此仅从其成员函数中使用.那些不需要修改(因此不需要分离)的成员函数应该是const它们自己,使成员对指针的访问权限在const上下文中,因此不会分离.

编辑以回答编辑:

根据我的理解,nullptr == testInst会调用

bool operator==(T const* a, T const* b)
Run Code Online (Sandbox Code Playgroud)

这种理解是不正确的.运营商的过载分辨率相当复杂,为参与分辨率的运营商的内置版本提供了大量代理签名.该过程在标准中的[over.match.oper]和[over.built]中描述.

具体来说,相关内置的相等候选者在[over.built] p16和17中定义.这些规则说明对于每个指针类型T,都operator ==(T, T)存在.现在,无论是int*const int*是指针类型,所以这两个相关的签名operator ==(int*, int*)operator ==(const int*, const int*).(也有operator ==(std::nullptr_t, std::nullptr_t),但不会被选中.)

为了区分这两个重载,编译器必须比较转换序列.对于第一个参数,nullptr_t -> int*nullptr_t -> const int*都是相同的; 它们是指针转换.将const其中一个指针添加到其中.(参见[conv.ptr].)对于第二个参数,转换分别为SharedPointer<int> -> int*SharedPointer<int> -> const int*.第一个是用户定义的转换,调用operator int*(),无需进一步转换.第二个是用户定义的转换,调用operator const int*() const,这需要首先进行资格转换才能调用const版本.因此,非const版本是首选.