由于 ADL 对模板化函数的调用不明确
我和我的同事都被这个问题困扰过几次。编译时
#include <deque>
#include <boost/algorithm/string/find.hpp>
#include <boost/operators.hpp>
template< class Rng, class T >
typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) {
return std::find( boost::begin(rng), boost::end(rng), t );
}
struct STest {
bool operator==(STest const& test) const { return true; }
};
struct STest2 : boost::equality_comparable<STest2> {
bool operator==(STest2 const& test) const { return true; }
};
void main() {
std::deque<STest> deq;
find( deq, STest() ); // works
find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function
}
...VS9 编译器在编译第二个查找时失败。这是因为 STest2
继承自 boost 命名空间中定义的类型,这会触发编译器尝试 ADL 来查找 boost::algorithm::find(RangeT& Input, const FinderT&Finder)
。
一个明显的解决方案是在对 find(…)
的调用前添加“::
”,但为什么这是必要的呢?全局命名空间中存在完全有效的匹配,那么为什么要调用参数依赖查找呢?有人可以解释一下这里的原理吗?
I've been bitten by this problem a couple of times and so have my colleagues. When compiling
#include <deque>
#include <boost/algorithm/string/find.hpp>
#include <boost/operators.hpp>
template< class Rng, class T >
typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) {
return std::find( boost::begin(rng), boost::end(rng), t );
}
struct STest {
bool operator==(STest const& test) const { return true; }
};
struct STest2 : boost::equality_comparable<STest2> {
bool operator==(STest2 const& test) const { return true; }
};
void main() {
std::deque<STest> deq;
find( deq, STest() ); // works
find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function
}
...the VS9 compiler fails when compiling the second find. This is due to the fact that STest2
inherits from a type that is defined in boost namespace which triggers the compiler to try ADL which finds boost::algorithm::find(RangeT& Input, const FinderT& Finder)
.
An obvious solution is to prefix the call to find(…)
with "::
" but why is this necessary? There is a perfectly valid match in the global namespace, so why invoke Argument-Dependent Lookup? Can anybody explain the rationale here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
ADL 不是“正常”重载解析失败时使用的后备机制,ADL 找到的函数与正常查找找到的函数一样可行。
如果 ADL 是后备解决方案,那么您可能很容易陷入使用某个函数的陷阱,即使存在另一个更匹配但只能通过 ADL 可见的函数。在(例如)运算符重载的情况下,这似乎特别奇怪。您不希望通过
operator==
比较两个对象的类型,当适当的位置存在完美的operator==
时,它们可以隐式转换为这些类型。命名空间。ADL isn't a fallback mechanism to use when "normal" overload resolution fails, functions found by ADL are just as viable as functions found by normal lookup.
If ADL was a fallback solution then you might easily fall into the trap were a function was used even when there was another function that was a better match but only visible via ADL. This would seem especially strange in the case of (for example) operator overloads. You wouldn't want two objects to be compared via an
operator==
for types that they could be implicitly converted to when there exists a perfectly goodoperator==
in the appropriate namespace.我会自己添加明显的答案,因为我刚刚对这个问题做了一些研究:
C++03 3.4.2
至少它符合标准,但我仍然不明白这里的基本原理。
I'll add the obvious answer myself because I just did some research on this problem:
C++03 3.4.2
At least it's standard conformant, but I still don't understand the rationale here.
考虑一个继承自
std::ostream
的mystream
。您希望您的类型支持通常在 std 命名空间中为std::ostream
定义的所有<<
运算符。因此基类是 ADL 的关联类。我认为这也遵循替换原则 - 类名称空间中的函数被视为其接口的一部分(请参阅 Herb Sutter 的“类中有什么?”)。因此,适用于基类的接口应该继续适用于派生类。
您还可以通过禁用 ADL 来解决此问题:
Consider a
mystream
which inherits fromstd::ostream
. You would like that your type would support all the<<
operators that are defined forstd::ostream
normally in the std namespace. So base classes are associated classes for ADL.I think this also follows from the substitution principle - and functions in a class' namespace are considered part of its interface (see Herb Sutter's "What's in a class?"). So an interface that works on the base class should remain working on a derived class.
You can also work around this by disabling ADL:
我认为你自己已经说明了问题:
全局命名空间中的函数被视为最后。根据定义,它是最外部的范围。。在更近的范围内(从调用的角度来看)找到的任何具有相同名称(不一定适用)的函数将首先被选取。
此处(除非
vector
或deque
包含algorithm
,这是允许的),在名称查找期间将选取的唯一方法是:如果
algorithm
以某种方式包含在内,还会有:当然,重载解析将失败,表明没有匹配。
请注意,名称查找非常愚蠢:既不考虑数量也不考虑参数类型!
因此,您应该在 C++ 中使用两种自由函数:
如果您不遵守这些规则,它可能会起作用,也可能不起作用,具体取决于其中包含的内容,这是非常尴尬的。
I think you stated the problem yourself:
Functions in the global namespace are considered last. It's the most outer scope by definition. Any function with the same name (not necessarily applicable) that is found in a closer scope (from the call point of view) will be picked up first.
Here (unless
vector
ordeque
includealgorithm
, which is allowed), the only method that will be picked up during name look-up will be:If
algorithm
is somehow included, there will also be:And of course, overload resolution will fail stating that there is no match.
Note that name-lookup is extremely dumb: neither arity nor argument type are considered!
Therefore, there are only two kinds of free-functions that you should use in C++:
If you fall out of these rules, it might work, or not, depending on what's included, and that's very awkward.