我几次被这个问题所困扰,所以我的同事也是如此。编译时
#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命名空间中定义的类型,触发编译器尝试找到boost::algorithm::find(RangeT& Input, const FinderT& Finder)
的ADL。
一个明显的解决方案是将find(…)
的调用加上“::
”前缀,但为什么这有必要?全局命名空间中存在完全有效的匹配,那么为什么要调用Argument-Dependent Lookup?谁能解释一下这里的理由呢?
答案 0 :(得分:7)
ADL不是“正常”重载解析失败时使用的回退机制,ADL找到的函数与正常查找找到的函数一样可行。
如果ADL是一个后备解决方案,那么即使有另一个功能更好匹配但只能通过ADL可见,你可能很容易陷入陷阱。在(例如)运算符重载的情况下,这似乎特别奇怪。您不希望通过operator==
对两个对象进行比较,以便在适当的命名空间中存在完美的operator==
时可以隐式转换为它们。
答案 1 :(得分:3)
我会自己添加明显的答案,因为我只是对这个问题进行了一些研究:
C ++ 03 3.4.2
§2对于函数调用中的每个参数类型T,都有一组零个或多个关联的命名空间[...]命名空间和类的集合按以下方式确定:
[...]
- 如果T是类类型(包括联合),则其关联的类是:类本身;它所属的类 会员,如果有的话; 及其直接和间接基类。其关联的命名空间是命名空间 其中定义了相关的类。
§2a如果名称的普通非限定查找找到类成员函数的声明,则关联 不考虑名称空间和类。否则通过查找找到的声明集 函数名是使用普通非限定查找和集合找到的声明集的并集 在与名称类型相关联的名称空间和类中找到的声明。
至少它符合标准,但我仍然不明白这里的理由。
答案 2 :(得分:3)
考虑从mystream
继承的std::ostream
。您希望您的类型支持通常在std命名空间中为<<
定义的所有std::ostream
运算符。因此基类是ADL的关联类。
我认为这也取决于替换原则 - 类'命名空间中的函数被认为是其接口的一部分(参见Herb Sutter的“类中有什么?”)。因此,在基类上工作的接口应该继续在派生类上工作。
您也可以通过禁用ADL解决此问题:
(find)( deq, STest2() );
答案 3 :(得分:1)
我认为你自己说过这个问题:
全局命名空间中的
全局命名空间中的函数被认为是最后一个。根据定义,它是最外层的范围 。将首先拾取在更近的范围内(从调用的角度)找到的具有相同名称(不一定适用)的任何功能。
template <typename Rng, typename T>
typename Rng::iterator find( Rng& rng, T const& t );
namespace foo
{
bool find(std::vector<int> const& v, int);
void method()
{
std::deque<std::string> deque;
auto it = find(deque, "bar");
}
}
此处(除非vector
或deque
包含允许的algorithm
),在名称查找期间将选择的唯一方法是:
bool foo::find(std::vector<int> const&, int);
如果以某种方式包含algorithm
,则还会有:
template <typename FwdIt>
FwdIt std::find(FwdIt begin, FwdIt end,
typename std::iterator_traits<FwdIt>::value_type const& value);
当然,重载解析将失败,说明没有匹配。
请注意,名称查找非常愚蠢:既不考虑arity也不考虑参数类型!
因此,在C ++中只应使用两种自由函数:
如果你不遵守这些规则,它可能会起作用,也可能不起作用,这取决于所包含的内容,这是非常尴尬的。