使用"运算符T *()"而不是" T * operator->()"成员访问

时间:2015-05-08 12:01:10

标签: c++ syntax type-conversion

表达式struct Elf要求x->y是指向完整类类型的指针,或者当x是类的实例时,需要为{{1}定义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 } 代替error: base operand of '->' has non-pointer type 'A'呢?

2 个答案:

答案 0 :(得分: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->函数返回值为   非类型。

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

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

答案 1 :(得分:0)

我没有手头的标准,也许有人可以进来并在我之后提出更好的答案。但是,从cppreference.com的叙述中可以看出:

  

内置运算符的左操作数。和操作员 - >是完整标量类型T(对于运算符。)或指向完整标量类型T *(对于运算符 - >)的表达式,在调用运算符之前对其进行求值。右操作数是T的成员对象或成员函数的名称或T的基类之一,例如, expr.member,可选地合格,例如expr.name::member,可选地使用模板消歧器,例如expr.template成员。   对于内置类型,表达式A-> B完全等同于(* A).B。 如果是用户定义的运算符 - >提供,运营商 - >再次调用它返回的值,直到operator->到达,返回一个普通指针。之后,内置语义应用于该指针。

重点是我的。

如果是运营商 - >将被另一个运算符的结果递归调用 - > (它将具有指针返回类型),它强烈暗示运算符 - >必须在指针类型上调用。