将成员函数更改为const会默默地中断代码

时间:2017-11-21 14:25:29

标签: c++

我正在为第三方库编写接口。它通过C接口操作对象,C接口本质上是void*。这是简化的代码:

struct LibIntf
{
    LibIntf() : opaquePtr{nullptr} {}

    operator void *()  /* const */ { return opaquePtr;  }     
    operator void **()             { return &opaquePtr; }

    void *opaquePtr;
};

int UseMe(void *ptr)
{
    if (ptr == (void *)0x100)
        return 1;
    return 0;
}

void CreateMe(void **ptr)
{
    *ptr = (void *)0x100;
}

int main()
{
    LibIntf lib;

    CreateMe(lib);
    return UseMe(lib);
}

const行添加operator void *()之前,一切正常。然后,代码默认默认使用operator void **()破解代码。

我的问题是为什么?

我通过一个不修改对象的函数返回一个指针。应该能够标记const。如果将其更改为const指针,编译器应该会出错,因为operator void **()不应该只是想要CallMe()的函数void *的匹配。< / p>

1 个答案:

答案 0 :(得分:6)

这是标准所说的应该发生的事情,但这远非显而易见。对于快速读者,请跳转到“如何解决它?”最后。

了解const重要的原因

添加const限定符后,当您使用UseMe实例调用LibIntf时,编译器具有以下两种可能性:

  1. LibIntf 1 LibIntf 2 void** 3 void* (通过operator void**()
  2. LibIntf 3 const LibIntf 2 void* 1 void* (通过operator void* const()
  3. 1)无需转换。
    2)用户定义的转换运算符 3)法律转换。

    这两个转换路径是合法的,那么选择哪一个?
    定义C ++答案的标准:

      

    [over.match.best]/1

         

    如下定义ICSi(F)

         
        
    • [...]
    •   
    • let ICSi(F)表示隐式转换序列,它将列表中的i参数转换为可行函数F的i参数的类型。[over.best.ics]定义隐式转换序列和[over.ics.rank]定义了一个隐式转换序列比另一个转换序列更好的转换序列或更差的转换序列。
    •   
         

    鉴于这些定义,可行函数F1被定义为a   如果适用于所有参数,则比另一个可行函数F2具有更好的功能   iICSi(F1)转换序列不比ICSi(F2)差,然后

         
        
    • 对于某些参数jICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果没有,

    •   
    • 上下文是由用户定义的转换初始化(请参阅[dcl.init][over.match.conv][over.match.ref])以及来自{{1的返回类型的标准转换序列到目标类型(即,正在初始化的实体的类型)是比从F1的返回类型到目标类型的标准转换序列更好的转换序列。

    •   

    (在获得它之前我必须阅读它几次。)

    这意味着在您的特定情况下比选项#1 更好而不是选项#2,因为对于用户定义的转换运算符,返回类型的转换({在选项#2中将参数类型(F2转换为void** 后,{1}}到选项#1中的void*被视为。< / p>

    在链中,这意味着在选项#1中没有任何内容可以转换(后者在转换链中会有,但尚未考虑)但在选项#2中,从LibIntf转换为{{1需要。因此,选项#1被称为更好

    如何解决?

    通过变量转换为const LibIntf(显式(强制转换),无需考虑non-constconst 转换总是明确的(或被称为转换))):

    non-const