那里发生了什么?
#include <functional>
namespace A {
struct Class { };
}
bool operator<(const A::Class& a, const A::Class& b)
{ return false; }
int main()
{
std::less<A::Class>()(A::Class(), A::Class());
return 0;
}
编译好了。但是,如果我使用。
#include <set>
我收到了错误:
g++ test.cc -o test
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_tree.h:64:0,
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/set:60,
from lookup.cc:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A::Class]':
test.cc:15:49: instantiated from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.2/include/g++-v4/bits/stl_function.h:230:22: error: no match for 'operator<' in '__x < __y'
make: *** [test] Error 1
答案 0 :(得分:11)
查找失败的原因是set
在operator<
命名空间中为std::set
引入了std
,隐藏了所有其他全局operator<
。
<
实例化中std::less
的查找发生在std
命名空间内的范围内。唯一可以使operator<
命名空间外的任何std
变为可见的方法是,如果ADL开始行动,这只会发生在最近的封闭命名空间中。
不包含<set>
,operator<
命名空间中没有std
引入(这可能是依赖于实现的)隐藏全局operator<
因此非operator<
ADL查找规则仍然可以找到采用A::Class
。
operator<
更正:正如@JohannesSchaub指出的那样,只有<functional>
的声明发生在std::less
之前(其中x < y
已定义),此分析才会正确首先包括在内。实际上,在模板定义中非ADL查找应该可见的 unqualified-id 调用的函数的唯一重载是在定义点可见的那些重载。在定义点和实例化点之间引入的定义应仅对ADL查找可见。 (在operator<
这样的表达式中搜索名为operator<
的候选函数,这是 unqualified-id 的一种特殊形式。)
就目前而言,重载的<set>
不应被视为包含或不包含{{1}}包含的候选者,尽管查找规则中的这些极端情况并非始终由所有编译器正确处理。
答案 1 :(得分:5)
operator<
也应该在namespace A
中,否则无法查找。
详细说明:
首先,只需在operator<
上的两个Class
对象上调用main()
,或者甚至实现自己的less
,无论operator<
是否在同一名称空间中作为Class
或不是operator<
,尽管应该位于同一名称空间中,因为每个人(包括库的实现者)都期望这样。 gcc和MSVC 2010(正如我刚刚测试的那样)在其标准库中包含了几个额外的In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_tree.h:65:0,
from /usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/set:60,
from test.cc:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h: In member function 'bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = A::Class]':
test.cc:9:42: instantiated from here
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h:234:22: error: no match for 'operator<' in '__x < __y'
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_function.h:234:22: note: candidates are:
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_pair.h:205:67: note: template<class _T1, class _T2> bool std::operator<(const std::pair<_T1, _T2>&, const std::pair<_T1, _T2>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_iterator.h:290:46: note: template<class _Iterator> bool std::operator<(const std::reverse_iterator<_Iterator>&, const std::reverse_iterator<_Iterator>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_iterator.h:340:47: note: template<class _IteratorL, class _IteratorR> bool std::operator<(const std::reverse_iterator<_IteratorL>&, const std::reverse_iterator<_IteratorR>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_tree.h:847:70: note: template<class _Key, class _Val, class _KeyOfValue, class _Compare, class _Alloc> bool std::operator<(const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&, const std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_set.h:712:46: note: template<class _Key, class _Compare, class _Alloc> bool std::operator<(const std::set<_Key, _Compare, _Alloc>&, const std::set<_Key, _Compare, _Alloc>&)
/usr/lib/gcc/x86_64-pc-linux-gnu/4.6.0-alpha20110115/include/g++-v4/bits/stl_multiset.h:695:51: note: template<class _Key, class _Compare, class _Alloc> bool std::operator<(const std::multiset<_Key, _Compare, _Alloc>&, const std::multiset<_Key, _Compare, _Alloc>&)
,编译器之间无法解析。
gcc 4.5.2的错误消息具有误导性。使用预发行版4.6编译相同内容,我得到更具体的
std::set<A::Class>
顺便提一下,SunCC(同时使用rw和stlport4)可以干净地编译它,甚至创建一个可用的{{1}}。
答案 2 :(得分:3)
您的operator<
重载需要与Class
位于同一名称空间中(也就是说,它也需要位于A
名称空间中)。
C ++具有复杂的名称查找规则。其中一个更有趣的“功能”是“依赖于参数的查找”或ADL,其中名称可以在与函数参数关联的名称空间中查找。对于没有基类且不是嵌套类的类(如Class
),唯一关联的命名空间是类所在的命名空间。
因此,与Class
关联的唯一命名空间是A
,因此在A
重载的依赖于参数的查找期间仅搜索operator<
命名空间。
Charles Bailey's answer可以很好地解释为什么在包含<set>
时只能看到问题。