使用"运算符T*()"代替"T*运算符 - >()"进行成员访问

phi*_*uru 4 c++ syntax type-conversion

表达式x->y需要x是指向完整类类型的指针,或者何时x是类的实例,需要operator->()定义x.但是当后者是这种情况时,为什么不能使用转换函数(x即将对象转换为指针)?例如:

struct A
{
    int mi;

    operator A*() { return this; }
};

int main()
{
    A a;

    a[1];  // ok: equivalent to *(a.operator A*() + 1);
    a->mi; // ERROR
}
Run Code Online (Sandbox Code Playgroud)

这会给出一条错误消息:

error: base operand of '->' has non-pointer type 'A'

但问题是,为什么不使用它a.operator A*(),就像它一样a[1]

bog*_*dan 5

这是由于表达式中运算符的特殊重载解析规则.对于大多数运算符,如果任一操作数具有类或枚举类型,则运算符函数和内置运算符相互竞争,并且重载决策确定将使用哪个操作符.这就是发生的事情a[1].但是,有一些例外情况,适用于您案件的例外情况见标准中的第[13.3.1.2p3.3]段(所有引文中的重点是:

(3.3) - 对于运算符,,一元运算符&或运算符->, 内置候选集是空的.对于所有其他运算符,内置候选项包括13.6中定义的所有候选运算符函数,与给定运算符相比,

  • 具有相同的运营商名称,和
  • 接受相同数量的操作数,和
  • 接受根据13.3.3.1可以转换给定操作数或操作数的操作数类型,和
  • 没有与非函数模板特化的非成员候选者相同的参数类型列表.

因此,对于a[1],用户定义的转换用于获取[]可以应用内置运算符的指针,但是对于那里的三个例外,仅首先考虑运算符函数(在这种情况下没有任何函数) ).后来,[13.3.1.2p9]:

如果操作员是操作员,,一元操作员&或操作员->,并且没有可行的功能,则假定操作员是内置操作员并根据第5章解释.

简而言之,对于这三个运算符,仅当其他所有操作都失败时才会考虑内置版本,然后他们必须在没有任何用户定义的转换的情况下处理操作数.

据我所知,这是为了避免混淆或模棱两可的行为.例如,内置运营商,&将是可行的(几乎)所有操作数,所以超载,如果他们将在重载的正常步骤中认为他们是行不通的.

运算符->在重载时具有异常行为,因为它可能导致一系列过载调用->,如[注释129]中所述:

如果函数返回的值operator->具有类类型,则可能导致选择并调用另一个operator->函数.重复该过程,直到operator->函数返回非类类型的值.

我想你有可能从一个重载的类开始->,它返回另一个类类型的对象,它不会重载->但是有一个用户定义的转换为指针类型,导致最终调用in ->被认为有点混乱.将此限制为显式重载->看起来更安全.


所有引用均来自当前工作草案N4431,但相关部分自C++ 11以来没有改变.