(所有测试均在 Microsoft (R( C/C++ 优化编译器版本 19.00.24215.1 上执行,适用于 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
}
根据重载解析规则(来自 CPP 首选项(
如果隐式,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> [///]sandboxsandboxsandbox.cpp(29): error C2666: 'myString::operator []': 3 overloads have similar conversions
1> [///]sandboxsandboxsandbox.cpp(19): note: could be 'const char &myString::operator [](unsigned int) const'
1> [///]sandboxsandboxsandbox.cpp(18): note: or 'char &myString::operator [](unsigned int)'
1> [///]sandboxsandboxsandbox.cpp(29): note: while trying to match the argument list '(myString, int)'
另请注意,使用if( str[0u] == myChar ) return 0;
或删除operator const char *( ) const
解决错误
为什么这里有错误,我在重载解决规则方面犯了什么错误?
编辑:这可能是此版本中的视觉C++错误,对此有任何明确的确认吗?
这是该问题的缩小版本,它重现了我扔给它的所有编译器。
#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
}
基本上,您有两个候选函数来获取bool operator==(char,char)
的char
:[over.match.oper]/3
char& myString::operator[]( unsigned ptrdiff_t )
([over.match.oper]/3.1 => [over.sub](char& operator[]( char*, ptrdiff_t)
([over.match.oper]/3.3 => [over.built]/14(
请注意,如果myString::operator[]
采用ptrdiff_t
而不是unsigned ptrdiff_t
,那么它将隐藏每个 [over.built]/1 的内置运算符。因此,如果您只想避免此类问题,只需确保任何operator[]
过载都采用整数值,ptrdiff_t
。我将跳过可行性检查[over.match.viable],直接进入转换排名。
char& myString::operator[]( unsigned ptrdiff_t )
对于重载,这被认为是具有前导隐式对象参数,因此要匹配的签名是(myString&, unsigned ptrdiff_t)
myString&
=>myString&
标准转换序列:标识(等级:完全匹配(- 直接绑定引用
ptrdiff_t
=>unsigned ptrdiff_t
标准转换序列:左值转换 ->积分转换(排名:转换(- 有符号左值到无符号 prvalue
char& operator[]( char*, ptrdiff_t)
myString&
=>char*
用户自定义转换序列:身份+operator char*(myString&)
请注意,根据 [over.match.oper]/7,我们不会获得第二个标准转换序列。
ptrdiff_t
=>ptrdiff_t
标准转换序列:标识(排名:完全匹配(
最佳可行功能
第一个参数
标准转换序列优于用户定义的转换序列([over.ics.rank]/2.1(
第二个参数
排名转换比排名完全匹配([over.ics.rank]/3.2.2(差
结果
我们无法满足要求
如果对于所有参数 i,ICSi(F1( 不是比 ICSi(F2( 更差的转换序列
所以这两个函数都不是更好的函数。
因此,根据 [over.match.best]/2,它是模棱两可的。
如何解决这个问题?
好吧,最简单的解决方案是永远不要让operator[]
重载的参数成为可以通过精确匹配排名转换以外的其他内容从ptrdiff_t
转换为的内容。查看转换表,这似乎意味着您应该始终将operator[]
成员函数声明为X& T::operator[]( ptrdiff_t )
。这涵盖了"像数组一样行动"的常见用例。如上所述,精确使用ptrdiff_t
甚至可以通过将内置下标运算符从表中移除来抑制对operator T*
候选项的搜索。
另一种选择是不为类定义T1 operator[]
和operator T2*
,其中T1
和T2
都可以满足(可能是隐式的(函数调用的相同参数。这涵盖了您将operator[]
用于聪明的语法事物的情况,并最终得到诸如T T::operator[](X)
之类的东西。例如,如果X::operator ptrdiff_t()
存在,T::operator T*()
也存在,那么你又是模棱两可的。
我能想象到T::operator T*()
的唯一用例是,如果你希望你的类型隐式转换为指向自身的指针,就像函数一样。别这样。。。