编译此程序时,我期待运算符<<调用以解析全局命名空间中的一个,但编译器报告一个模糊的重载。我认为非依赖查找发生在名称空间中的函数之前,由于参数依赖查找而被包含为潜在匹配。这似乎是非模板函数的情况。
有人可以解释一下吗?
#include <iostream>
class Foo
{};
namespace NS
{
class Stream
{};
template <typename T>
Stream& operator << ( Stream& s, T t)
{
std::cerr << "Namespace call!\n";
return s;
}
}
template<typename STREAM>
STREAM& operator << ( STREAM& s, Foo f )
{
std::cerr << "Global NS call";
return s;
}
/**
* This function (as opposed to the one above) is not ambiguous. Why?
NS::Stream& operator << ( NS::Stream& s, Foo f )
{
std::cerr << "Global NS call";
return s;
}
*/
int main()
{
Foo f;
NS::Stream s;
s << f;
return 0;
}
编译器输出:
test11.cpp: In function ‘int main()’:
test11.cpp:28: error: ambiguous overload for ‘operator<<’ in ‘s << f’
test11.cpp:18: note: candidates are: STREAM& operator<<(STREAM&, Foo) [with STREAM = NS::Stream]
test11.cpp:13: note: NS::Stream& NS::operator<<(NS::Stream&, T) [with T = Foo]
答案 0 :(得分:3)
s << f
有两种可能的候选者:全局名称和命名空间名称。
答案 1 :(得分:1)
全局命名空间没有特别优先权。 s << f
的问题是两个参数都与命名空间相关联:s
::NS
和f
::
。< / p>
鉴于全局命名空间与其他命名空间一样(除了它总是在范围内,这在这里无关紧要),两个函数重载与最佳匹配完全相关,编译器无法做到。
使用IOStreams库时,可以通过接受istream &
或ostream &
类型的参数来解决此问题,而无需模板参数化。
答案 2 :(得分:1)
虽然这是一个老问题,但我认为还有一些事情尚未得到有关OP的具体问题的澄清,所以我们走了。
首先:依赖于参数的查找和正常的非限定查找都是为非限定函数和操作符调用完成的。这适用于普通函数和函数模板特化。根据[3.4.2第3段],唯一的例外是正常的非限定查找:
仅在上述情况下,不执行与参数相关的查找。如您所见,在这种情况下,这些都不适用。
因此,两个声明都被找到了。现在,需要执行重载解析来选择最佳可行功能。在两种情况下,参数都完美地拟合了参数类型,因此基于更好的转换,不能选择一个重载而不是另一个重载。两者都是模板特化,因此,作为最后的手段,功能模板的部分排序用于确定一个是否比另一个更专业。唉,NS中的模板更专用于第一个参数,而全局模板更专用于第二个参数,因此没有模板比另一个更专业。结论:不能选择任何重载,调用是模糊的。
现在,关于你的第二个问题,关于被注释掉的运营商定义。如果取消注释该定义,在这种情况下,也会执行ADL; 通过名称查找找到所有三个重载。同样,所有参数都完美匹配参数类型。与众不同的是,最后一个定义是普通的操作符函数,而不是模板。如果基于转换不能选择其他重载,那么,如果一个是正常函数而所有其他都是模板特化,则非模板优先于其他模板。这就是为什么在这种情况下电话不再含糊不清的原因。
标准参考文献是N4140,这是发布之前的最后一个C ++ 14草案,但我不认为自C ++ 03以来发生过任何变化。
答案 3 :(得分:0)
由于您在Stream
内定义了namespace NS
,因此存在歧义。如果在全局命名空间中定义Stream
,那么就不存在歧义。
编译器将尝试根据非限定函数及其关联名称空间的参数来解析要选择的函数。请参阅ISO / IEC 14882:2003标准的第3.4.2节 - 参数依赖名称查找。由于在全局命名空间中定义了一个参数,并且在NS中定义了一个参数,因此编译器不知道要使用哪个函数。