(所有测试均在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)
根据1),如果隐含的话,F1被确定为比F2更好的功能 F1的所有参数的转换都不比隐含的更糟糕 F2和
的所有参数的转换1)至少有一个 F1的论证,其隐式转换优于 F2参数的相应隐式转换
2)或。如果 不是这样,(仅在通过转换进行非类初始化的上下文中), 标准转换序列从F1的返回类型到 正在初始化的类型优于标准转换序列 来自F2的返回类型
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 ++错误,对此有任何明确的确认?
答案 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
bool operator==(char,char)
([over.match.oper]/3.1 =&gt; [over.sub])char& myString::operator[]( unsigned ptrdiff_t )
([over.match.oper]/3.3 =&gt; [over.built]/14)请注意,如果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
标准转化顺序:身份(排名:完全匹配)
标准转换序列优于用户定义的转换序列([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*()
的唯一用例是你希望你的类型隐式转换为指向自身的指针,就像一个函数。 不要这样做......