C++ 使用「;运算符T*()”;而不是",;T*运算符->;()";供会员使用

C++ 使用「;运算符T*()”;而不是",;T*运算符->;()";供会员使用,c++,syntax,type-conversion,C++,Syntax,Type Conversion,表达式x->y需要x作为完整类类型的指针,或者当x是类的实例时,需要为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 }

表达式
x->y
需要
x
作为完整类类型的指针,或者当
x
是类的实例时,需要为
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
}
这将显示一条错误消息:

错误:'->'的基操作数具有非指针类型“A”


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

我手头没有标准,也许有人可以进来跟我说一个更好的答案。然而,从cppreference.com上的叙述来看:

内置运算符的左操作数。运算符->是完全标量类型T(对于运算符)的表达式,或指向完全标量类型T*(对于运算符->)的指针,在调用运算符之前对其求值。右操作数是T的成员对象或成员函数的名称,或T的基类之一的名称,例如expr.member(可选限定),例如expr.name::member(可选使用模板消歧器),例如expr.template member。 对于内置类型,表达式A->B与(*A).B完全等效如果提供了用户定义的运算符->,则会递归调用运算符->返回的值,直到到达返回普通指针的运算符->为止。之后,内置语义将应用于该指针。

重点是我的


如果要对另一个运算符->的结果递归调用运算符->(该运算符将具有指针返回类型),则强烈暗示必须对指针类型调用运算符->。

这是由于表达式中运算符的特殊重载解析规则。对于大多数运算符,如果任一操作数的类型为类或枚举,则运算符函数和内置运算符将相互竞争,重载解析将确定将使用哪一个运算符。这就是
a[1]
的情况。但是,也有一些例外情况,适用于您的情况的例外情况见本标准第[13.3.1.2p3.3]段(所有引用中均强调我的情况):

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

  • 具有相同的操作员名称,并且
  • 接受相同数目的操作数,并且
  • 接受可根据13.3.3.1将给定操作数转换为的操作数类型,以及
  • 不具有与任何非函数模板专用化的非成员候选项相同的参数类型列表
因此,对于
a[1]
,用户定义的转换用于获取一个指针,内置的
[]
操作符可以应用到该指针,但是对于上面的三个异常,只会首先考虑操作符函数(在这种情况下没有)。稍后,[13.3.1.2p9]:

如果运算符是运算符
、一元运算符
&
,或 运算符
->
,并且没有可行的函数,则运算符为 假设为内置运算符,并根据 第5条

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

据我所知,这样做是为了避免混淆或模棱两可的行为。例如,内置运算符
&
对于(几乎)所有操作数都是可行的,因此,如果在重载解析的正常步骤中考虑它们,则重载它们将不起作用

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

如果
运算符->
函数返回的值具有类类型,则 可能导致选择并调用另一个
操作符->
函数。这个 该过程重复执行,直到
运算符->
函数返回的值为 非类类型

我假设您可能从一个重载
->
的类开始,该类返回另一个类类型的对象,该类不重载
->
,但具有用户定义的到指针类型的转换,从而最终调用内置的
->
,这被认为有点太混乱了。将此限制为显式重载
->
看起来更安全



所有引用均来自当前工作草案N4431,但相关部分自C++11以来未发生变化。

*(a.operator a*()+1)
与您的示例中的
a[1]
不等价。嗨@Axalo,我不知道这里有什么问题,请您指出?thx
a.运算符a*()
导致指向
a.的指针。
因此
*(a.运算符a*()+1)
将导致
*(&a+1)
。另一方面,
a[1]
返回a。和
a!=*(&a+1)
@Axalo“另一方面,a[1]返回“-不,它不返回”。您可能没有看到“#if 0…#endif”对。我希望我可以对编辑进行升级。谢谢你,巴里,我想你是误读了。“如果要对另一个运算符->的结果递归调用运算符->(该运算符将具有指针返回类型)”-则不要求
运算符->
具有指针返回类型。你引用的章节特别解释了在这种情况下会发生什么。这正是我想知道的,谢谢