namespace A{
struct A{
inline void operator+(const int B) {}; // 1)
};
inline void operator+(const A& a,const int B) {}; // 2)
}
inline void operator+(const A::A& a,const int B) {}; // 3)
int main()
{
A::A a;
a+1; // compile questions
return 1;
}
上述代码可以毫无问题地编译。
但是如果1)被评论,则由于“a + 1”中的'运算符+'在2)和3)处的模糊重载而无法编译。我可以理解,操作符+是在课堂上首次搜索的,如果1)没有被评论,没有问题需要编译。我不对吗?
主要问题:如果1)被评论,为什么编译器找到了一个匹配的运算符+,它继续找到其他运算符?我也很好奇首先应该找到哪一个。 (我认为它应该根据信息How does the operator overload resolution work within namespaces?)
立即停止第二个问题:
namespace A{
struct A{
inline void operator+(const long int B) {}; // 4), the input parameter type has been changed to long int
};
inline void operator+(const A& a,const int B) {}; // 5)
void test();
}
inline void operator+(const A::A& a,const int B) {}; // 6)
void A::test()
{
A a; // a)
a+ 1;
}
int main()
{
A::A a;
a+1; // b)
return 1;
}
a)由于4)和5)编译模糊错误。
b)编译模糊错误由于4),5)和6)。
问题i)
对于a)和b),为什么编译器已经在4)找到了operator + with(const long int input parameter type),它是struct A中的成员运算符函数,它仍然会继续查找其他函数。在我看来,编译器应该停在那里给出类型错误信息。它应该与memeber fucntion的输入参数类型完全匹配的情况相同,它会停止搜索其他类型。
问题2)
我认为编译器应该停在4)并给出类型错误信息。 在它继续的情况下,为什么成员函数重载函数(long int)具有与非memmber相同的分辨率排名和精确匹配的输入参数?在这种情况下,如果编译器决定继续搜索,我认为具有完全匹配输入参数的非成员情况应该获胜,这更有意义。
答案 0 :(得分:1)
它在C ++标准中解释,部分" 13.3.1.2表达式中的运算符",第2点:
如果任一操作数的类型是类或枚举,则a 可以声明用户定义的操作符函数来实现它 可能需要运算符或用户定义的转换来转换 操作数适用于内置运算符的类型。
根据进一步的解释,a+1
将在a.operator+(1)
(成员函数)或operator+(a,1)
(非成员函数)中进行转换。
第3点解释说:
表示二元运算符@,其左操作数的类型为 cv-unqualified version是T1和一个类型的右操作数 cv-unqualified version是T2,三套候选函数, 指定成员候选人,非成员候选人和内置 候选人的构建如下:
- 如果T1是完整的类类型,则候选成员集合是T1 :: operator @
的限定查找的结果
这解释了为什么(1)在激活时被选中。当您将其注释掉时,成员候选人集合为空。然后,根据标准中的下一个项目符号:
- 非成员候选者的集合是在表达式的上下文中根据运算符@的非限定查找的结果。 通常的非限定函数调用中的名称查找规则,除了 所有成员函数都被忽略。
根据这些规则,由于您的表达式位于main()
中且未包含在命名空间中,因此找到了两个候选函数,当然,它对于编译器来说是不明确的。
您可以轻松消除使用哪个运算符与main()
中的以下语句:
using A::operator+;
如果在命名空间A中的函数内部使用表达式a + 1,则不存在歧义:
//...your code preceding the main()
namespace A{ // reopen the namespace
void f()
{
A a;
a + 1; // version of the namespace is taken first
}
}
将首先获取在命名空间中定义的运算符(只要(1)仍然被注释掉)。
关于第二个问题
第一句话:如果成员函数使用int
作为参数,就像其他两个函数一样,没有歧义,并且(4)将在第一个问题中为两个表达式选择。如果所有三个函数都使用long
作为参数而不是int
,则会发生同样的情况。
您拥有相同名称但不同的参数这一事实要求我们在标准的第13.3.1.2节中深入挖掘名称隐藏和重载规则。第6点指出:
重载解析的候选函数集是。的并集 会员候选人,非会员候选人和内置人员 候选人。参数列表包含的所有操作数 运营商。候选函数集中的最佳函数是 根据13.3.2和13.3.3选择
13.3.2是关于可行的函数,即具有相同数量的参数以及参数类型和参数类型之间的隐式转换。
13.3.3是关于选择最佳可行功能。 " 如果只有一个可行功能比所有其他可行功能更好,那么它就是 一个由重载决议选择的;否则电话不正确"。
在(a)的情况下,有两个最可行的函数:带有转换的成员函数(4)或没有转换的非成员函数,在命名空间(5)中(因为test()在命名空间中) 。因此含糊不清。
在b)的情况下,有三个最佳可行功能:与(a)和(6)中相同。但请注意,如果(6)使用long,则不会导致歧义,因为成员函数会获胜。
答案 1 :(得分:-1)
解析对函数+的调用,编译器执行参数依赖查找,其中包括构造一组候选函数和重载决策。候选函数包括成员,非成员和内置候选者,非成员候选者包括由非限定名称查找找到的所有声明。如果未注释1,则正常查找限定名称将查找匹配的类成员函数。如果注释为1,则ADL查找与函数参数类型关联的名称空间集。它找到两个候选者,它们都是可行的函数并且具有相同的隐式转换序列排名,这导致模糊的过载错误。这里给出了同样的例子http://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism