模板和参数相关查找
编译这个程序时,我期待运算符<<调用解析为全局命名空间中的一个,但编译器却报告了一个不明确的重载。我认为非依赖查找发生在命名空间中的函数之前,这些函数由于参数依赖查找而被包含为潜在匹配项。对于非模板函数来说似乎就是这种情况。
有人可以解释一下吗?
#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]
When compiling this program, I was expecting the operator<< call to resolve to the one in the global namespace, but instead, the compiler reports an ambiguous overload. I thought non-dependent lookup occurred before the functions in namespaces that are included as potential matches due to argument dependent lookup. This seems to be the case for non-template functions.
Can someone explain?
#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;
}
Compiler Output:
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]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
s << 有两个可能的候选者。 f
:全局的和命名空间的。对于 C++ 编译器来说,这两者之间没有什么可以选择的,所以它是不明确的。There are two possible candidates for
s << f
: the global one and the namespace'd one. There is nothing to choose between those two for the C++ compiler, so it is ambiguous.尽管这是一个老问题,但我认为关于OP的具体问题仍有一些问题尚未澄清,所以我们开始吧。
首先:依赖参数的查找和正常的不合格查找都是针对不合格的函数和运算符调用进行的。这适用于普通函数和函数模板特化。根据 [3.4.2 第 3 段],唯一的例外是正常的非限定查找发现:
仅在上述情况下,不执行参数相关的查找。正如您所看到的,这些都不适用于本例。
因此,两个声明都找到了。现在,需要执行重载决策来选择最佳可行函数。在这两种情况下,参数都完美地适合参数类型,因此不能基于更好的转换来选择一种重载而不是另一种重载。两者都是模板特化,因此,作为最后的手段,函数模板的部分排序用于尝试确定一个模板是否比另一个更专业。唉,NS 中的模板更专门化第一个参数,而全局模板更专门化第二个参数,因此没有哪个模板比另一个更专门化。结论:没有重载可以选择超过其他,调用是不明确的。
现在,对于第二个问题,关于注释掉的运算符定义。如果取消对该定义的注释,在这种情况下也会执行 ADL; 所有三个重载都是通过名称查找找到的。同样,所有参数都与参数类型完美匹配。不同的是,最后一个定义是普通的运算符函数,而不是模板。如果基于转换无法选择任何重载,那么,如果一个函数是普通函数,而所有其他函数都是模板特化,则非模板函数优先于其他函数。这就是为什么在这种情况下调用不再含糊不清。
标准参考是 N4140,即发布前的最后一个 C++14 草案,但我认为自 C++03 以来上述内容没有任何变化。
Even though this is an old question, I think there are still some things that haven't been clarified regarding the OP's specific questions, so here we go.
First of all: argument-dependent lookup and normal unqualified lookup are both done for unqualified function and operator calls. This applies to both normal functions and function template specializations. According to [3.4.2 paragraph 3], the only exceptions are if normal unqualified lookup finds:
Only in the cases above, argument-dependent lookup is not performed. As you can see, none of these apply in this case.
So, both declarations are found. Now, overload resolution needs to be performed to choose the best viable function. The arguments fit the parameter types perfectly in both cases, so one overload cannot be chosen over another based on better conversions. Both are template specializations, so, as a last resort, partial ordering of function templates is used to try to determine if one is more specialized than the other. Alas, the template in NS is more specialized for the first argument, and the global one is more specialized for the second argument, so no template is more specialized than the other. Conclusion: no overload can be chosen over the other, the call is ambiguous.
Now, for your second question, regarding the operator definition that's commented out. If you uncomment that definition, in this case, too, ADL is performed; all three overloads are found by name lookup. Again, all arguments match the parameter types perfectly. What's different is that the last definition is a normal operator function, not a template. If no overload can be chosen over the others based on conversions, then, if one is a normal function and all the others are template specializations, the non-template one is preferred over the others. That's why the call is no longer ambiguous in this case.
Standard references are to N4140, the last C++14 draft before publication, but I don't think any of the above changed since C++03.
全局命名空间没有特殊的优先级。
s << 的问题f
是两个参数都与命名空间关联:s
与::NS
和f
与::
。鉴于全局命名空间与其他命名空间一样(除了它始终在作用域内,这在这里并不重要),两个函数重载完全绑定在一起以实现最佳匹配,并且编译器无能为力。
使用 IOStreams 库时,通过接受
istream &
或ostream &
类型的参数来解决此问题,无需模板参数化。The global namespace has no special priority. The problem with
s << f
is that both arguments are associated with a namespace:s
with::NS
andf
with::
.Given that the global namespace is just like any other (except that it is always in scope, which does not matter here), the two function overloads are exactly tied for best match and there is nothing the compiler can do.
When using the IOStreams library, this problem is resolved by accepting parameters of type
istream &
orostream &
, without template parameterization.存在歧义,因为您在
namespace NS
中定义了Stream
。如果你在全局命名空间中定义Stream
,那么就不会有歧义。编译器将尝试根据非限定函数的参数及其关联的命名空间来决定选择哪个函数。请参阅 ISO/IEC 14882:2003 标准的第 3.4.2 节 - 参数相关名称查找。由于在全局命名空间中定义了一个参数,在 NS 中定义了一个参数,因此编译器不知道要使用哪个函数。
There is ambiguity because you have defined
Stream
insidenamespace NS
. If you defineStream
in global namespace, then there will be no ambiguity.The compiler will try to resolve which function to choose according to the arguments to the unqualified function and their associated namespaces. Refer to Section 3.4.2 - Argument-dependent name lookup, of ISO/IEC 14882:2003 standard. As one argument is defined in global namespace and one argument is defined in NS, the compiler does not know which function to use.