考虑以下类别:
class Foo
{
public:
void operator [] (const std::string& s) { }
void operator [] (std::size_t idx) { }
};
在此,给定的实例Foo f
,该表达式f[0]
不是模棱两可的,因为编译器选择了第二个重载。同样,表达式f["abc"]
也不是模棱两可的,因为编译器会选择第一个重载(因为aconst char*
可以转换为an std::string
)。
那么,为什么要这样呢?如果我们有两个基类,每个基类都有不同的重载,突然之间就会有歧义?
假设我们有:
class Base1
{
public:
void operator [] (const std::string& s) { }
};
class Base2
{
public:
void operator [] (std::size_t idx) { }
};
class Derived : public Base1, public Base2
{ };
现在,如果我们说:
Derived d;
d[0];
编译器抱怨:
error: request for member ‘operator[]’ is ambiguous
d[0];
^
note: candidates are: void Base2::operator[](std::size_t)
void operator [] (std::size_t idx) { }
^
note: void Base1::operator[](const string&)
void operator [] (const std::string& s) { }
为什么两个运算符重载现在都在基类中的事实导致任何歧义?有解决此问题的方法吗?
编辑:这可能是编译器错误(我正在使用GCC 4.8.1)
这不是过载解析的问题,而是成员名查找(在10.2中定义)。考虑(因为我不想operator[]
在任何地方写):
struct base1 { void f(int); };
struct base2 { void f(double); };
struct derived : base1, base2 {};
int main() {
derived d; d.f(0);
}
当f
在postfix表达式中开始查找时d.f(0)
,它将首先调查derived
并发现f
根本无法解决任何问题。然后,10.2 / 5要求并行进行所有基类的查找,并构造单独的查找集。在这种情况下,S(f,base1)= {base1 :: f}和S(f,base2)= {base2 :: f}。然后按照10.2 / 6中的规则合并这些集合。第一个项目符号涉及当集合中的一个为空时,或者如果对不同集合的查找以相同成员结尾(考虑到它具有共同的基数),则合并。第二个项目符号很有趣,因为它适用于此处
10.2 / 6项目符号2
否则,如果S(f,Bi)和S(f,C)的声明集不同,则合并是不明确的:新的S(f,C)是具有无效声明集和子对象的并集的查找集。套。在后续合并中,无效的声明集被认为与其他声明集不同。
也就是说,S(f,base1)与S(f,base2)不同,因此S(f,derived)成为无效的声明集。并且查找失败。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句