c ++重载分辨率和常量

时间:2017-07-27 14:02:25

标签: c++ language-lawyer overload-resolution

(所有测试均在Microsoft(R)C / C ++优化编译器版本19.00.24215.1 for x86上执行)

考虑这个最小的例子:

struct myString
{
    operator const char *( ) const { return &dummy; }
    char& operator[]( unsigned int ) { return dummy; }
    const char& operator[]( unsigned int ) const { return dummy; }

    char dummy;
};

int main()
{
    myString str;
    const char myChar = 'a';

    if( str[(int) 0] == myChar ) return 0; //error, multiple valid overloads
}

根据重载决策规则(来自cppreference)

  

如果隐含的话,F1被确定为比F2更好的功能   F1的所有参数的转换都不比隐含的更糟糕   F2和

的所有参数的转换      

1)至少有一个   F1的论证,其隐式转换优于   F2参数的相应隐式转换

     

2)或。如果   不是这样,(仅在通过转换进行非类初始化的上下文中),   标准转换序列从F1的返回类型到   正在初始化的类型优于标准转换序列   来自F2的返回类型

根据1),

char& operator[]( unsigned int )应该更好。

在两个参数(this = myString)中根本不需要转换,而operator const char *( ) const将其转换为const char *并且const char& operator[]( unsigned int ) const将其转换为const myString,因此有一个参数没有任何隐式转换,恰好是最好的转换

但是我的编译器会出现以下错误:

1>  [///]\sandbox\sandbox\sandbox.cpp(29): error C2666: 'myString::operator []': 3 overloads have similar conversions
1>  [///]\sandbox\sandbox\sandbox.cpp(19): note: could be 'const char &myString::operator [](unsigned int) const'
1>  [///]\sandbox\sandbox\sandbox.cpp(18): note: or       'char &myString::operator [](unsigned int)'
1>  [///]\sandbox\sandbox\sandbox.cpp(29): note: while trying to match the argument list '(myString, int)'

另请注意,使用if( str[0u] == myChar ) return 0;或删除operator const char *( ) const可解决错误

为什么这里出现错误以及我对重载决策规则的错误是什么?

编辑:它可能是这个版本中的一个可视C ++错误,对此有任何明确的确认?

1 个答案:

答案 0 :(得分:1)

这是问题的缩小版,可以在all compilers I threw at it上重现。

#include <stddef.h>

struct myString
{
    operator char *( );
    char& operator[]( unsigned ptrdiff_t );
};

int main()
{
    myString str;
    if( str[(ptrdiff_t) 0] == 'a' ) return 0; //error, multiple valid overloads
}

基本上,您有两个候选函数来获取char的{​​{1}}:[over.match.oper]/3

请注意,如果char& operator[]( char*, ptrdiff_t)使用myString::operator[]代替ptrdiff_t,则会隐藏每[over.built]/1的内置运算符。因此,如果你想要做的只是避免这样的问题,只需确保任何带有整数值的unsigned ptrdiff_t重载,需要operator[]

我会跳过可行性检查[over.match.viable],然后直接进入转换排名。

ptrdiff_t

对于重载,这被认为具有前导隐式对象参数,因此要匹配的签名是 char& myString::operator[]( unsigned ptrdiff_t )

(myString&, unsigned ptrdiff_t) =&gt; myString&

标准转化顺序: Identity (排名:完全匹配) - 直接绑定参考

myString& =&gt; ptrdiff_t

标准转化顺序: Lvalue Transformation - &gt; Integral conversion (排名:转换) - 签名左值为无符号的首值

unsigned ptrdiff_t

char& operator[]( char*, ptrdiff_t) =&gt; myString&

User-defined conversion sequence Identity + operator char*(myString&)

请注意,根据[over.match.oper]/7,我们无法获得第二个标准转换序列。

char* =&gt; ptrdiff_t

标准转化顺序:身份(排名:完全匹配

Best viable function

第一个参数

标准转换序列优于用户定义的转换序列([over.ics.rank]/2.1

第二个论点

排名转换比排名完全匹配([over.ics.rank]/3.2.2

更糟糕

结果

我们无法满足requirement

  

如果对于所有参数i,ICSi(F1)的转换序列不比ICSi(F2)差

所以这两种功能都不是更好的功能。

因此,根据[over.match.best]/2含糊不清

如何解决这个问题?

嗯,最简单的解决方案是从不ptrdiff_t重载的参数可以通过以外的其他内容转换为operator[]完全匹配 - 已转换。查看the conversions table似乎意味着您应该始终将ptrdiff_t成员函数声明为operator[]。这涵盖了“像阵列一样行动”的通常用例。如上所述,精确使用X& T::operator[]( ptrdiff_t )会使内置下标运算符脱离表格,从而抑制ptrdiff_t候选的搜索

另一种选择是不为该类定义operator T*T1 operator[],其中operator T2*T1可能都满足相同的参数a(可能是隐式的) )函数调用。这包括您使用T2来处理聪明的句法事物的情况,并最终使用T T::operator[](X)之类的内容。例如,如果operator[]存在,X::operator ptrdiff_t()也存在,则您再次模糊不清。

我可以想象T::operator T*()的唯一用例是你希望你的类型隐式转换为指向自身的指针,就像一个函数。 不要这样做......