C++ 重载分辨率和恒常性

c++ overload resolution and constness

本文关键字:常性 分辨率 重载 C++      更新时间:2023-10-16

(所有测试均在 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*,其中T1T2都可以满足(可能是隐式的(函数调用的相同参数。这涵盖了您将operator[]用于聪明的语法事物的情况,并最终得到诸如T T::operator[](X)之类的东西。例如,如果X::operator ptrdiff_t()存在,T::operator T*()也存在,那么你又是模棱两可的。

我能想象到T::operator T*()的唯一用例是,如果你希望你的类型隐式转换为指向自身的指针,就像函数一样。别这样。。。