首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >比较运算符==中的共享指针恒变性

比较运算符==中的共享指针恒变性
EN

Stack Overflow用户
提问于 2018-03-08 08:31:45
回答 3查看 161关注 0票数 0

我无意中发现了我正在使用的共享指针的意外行为。

共享指针实现引用计数和分离(例如,复制),如果必要的话,包含在非连续使用上的实例。

为此,对于每个getter函数,智能指针都有一个const和一个non-const版本,例如:operator T *()operator T const *() const

问题:将指针值与nullptr进行比较会导致分离。

期望值:,我认为比较运算符总是会调用const版本。

简化示例:

(这个实现没有引用计数,但仍然显示了问题)

代码语言:javascript
复制
#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
}

问题1:为什么调用函数的非const版本应该足够调用const版本?

问题2:为什么可以称为非const版本?比较运算符(特别是与不可变的nullptr相比)不总是对const引用进行操作吗?

请记录在案:

我使用的共享指针是Qt的QSharedDataPointer,它保存了一个QSharedData-derived实例,但这个问题不是Qt特有的。

编辑:

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

代码语言:javascript
复制
bool operator==(T const* a, T const* b)

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

其中应援引:

代码语言:javascript
复制
inline operator const T *() const 

进一步提问:

,所以这个问题归结为:

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

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-03-08 08:36:25

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

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

代码语言:javascript
复制
const SharedPointer<int>& constRef = testInst;
eq = nullptr == constRef;

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

对于QSharedDataPointer的预期使用,这种行为通常不是一个问题。它意味着它是facade类的成员,因此只能从它的成员函数中使用。那些不需要修改(因此不需要分离)的成员函数应该是const本身,从而使对指针的成员访问在const上下文中而不是分离。

编辑以回答编辑:

据我理解,nullptr == testInst将调用 bool operator==(T const* a,T const* b)

这种理解是不正确的。对于操作符来说,过载解析是相当复杂的,它包含了大量的代理签名,用于内置版本的操作符参与解决方案。这个过程在标准中用over.match.oper和over.built描述。

具体来说,在over.builtp16和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版本是首选的.

票数 5
EN

Stack Overflow用户

发布于 2018-03-08 09:35:16

这是因为表达式testInst == nullptr是如何解析的:

  1. 让我们看一下类型: testInstSharedPointer<int>型的。 nullptr (为了简化起见)是T*void*类型,这取决于用例。 所以这个表达式读SharedPointer<int> == int*
  2. 我们需要有相同的类型来调用比较运算符。有两种可能性:
    1. 决心要int* == int*。 这涉及对operator int *()operator int const *() const的调用。 需要引证
    2. 解决SharedPointer<int> == SharedPointer<int>问题 这需要给SharedPointer(nullptr)打个电话。

  1. 因为第二个选项将创建一个新对象,而第一个选项不创建一个新对象,所以第一个选项是更好的匹配。需要引证
  2. 现在,在解析bool operator==(int [const] *a, int [const] *b) ( [const]是无关的)之前,必须将testInst转换为int*。 这涉及对转换operator int *()operator int const *() const的调用。 在这里,非const版本将被调用,因为testInst不是const。需要引证

我创建了一个建议,将T*的比较操作符添加到Qt:https://bugreports.qt.io/browse/QTBUG-66946QSharedDataPointer<T>中。

票数 0
EN

Stack Overflow用户

发布于 2018-03-08 09:46:58

也许这段代码可以让您了解发生了什么:

代码语言:javascript
复制
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;
}

输出是

代码语言:javascript
复制
1
2
3
4

如果抛出了cast对象(或对其的引用),则选择const cast运算符(case 3和4),反之亦然。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49168637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档