繁体   English   中英

关于std :: less行为的问题

[英]Question about std::less behavior

那里发生了什么?

#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

该查找失败的原因是set引入了operator<std::setstd隐藏所有其他的全局命名空间operator<

std::less的实例化中查找<std命名空间内的范围内发生。 任何operator< std命名空间之外的唯一方法是ADL开始行动,这只发生在最近的封闭命名空间中。

在不包含<set>std命名空间中没有operator<引入(这可能是依赖于实现的)隐藏了全局operator<因此非ADL查找规则仍然可以找到全局operator<哪个采用A::Class

更正:正如@JohannesSchaub指出的那样,只有在第一次包含<functional> (其中定义了std::less )之前发生了operator<的声明时,此分析才是正确的。 实际上,在模板定义中非ADL查找应该可见的非限定id调用的函数的唯一重载是在定义点可见的那些重载。 在定义点和实例化点之间引入的定义应仅对ADL查找可见。 (在一个表达式中,例如x < y候选函数名为operator<被搜索,这是unqualified-id的一种特殊形式。)

就目前而言,重载operator<不应被视为具有或不具有<set> include的候选者,尽管查找规则中的这些极端情况并非总是由所有编译器正确处理。

operator<应该在namespace A中,否则无法查找。

详细信息:首先,简单地从main()调用operator< on两个Class对象,或者甚至实现自己的less ,无论operator<是否与Class在同一个命名空间中都有效,尽管它应该在同一个命名空间中这就是每个人,包括图书馆的实施者所期望的。 gcc和MSVC 2010(我刚刚测试过)都在其标准库中包含了几个额外的operator< ,编译器之间无法解析。

gcc 4.5.2的错误消息具有误导性。 使用预发行版4.6编译相同内容,我得到的更具体

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>&)

顺便说一下,SunCC(同时使用rw和stlport4)可以干净地编译它,甚至可以创建一个可用的std::set<A::Class> <A :: Class>。

您的operator< overload需要与Class位于同一名称空间中(也就是说,它也需要位于A名称空间中)。

C ++具有复杂的名称查找规则。 其中一个更有趣的“功能”是“依赖于参数的查找”或ADL,其中名称可以在与函数参数关联的名称空间中查找。 对于没有基类且不是嵌套类的类(如Class ,唯一关联的命名空间是类所在的命名空间。

因此,与Class关联的唯一命名空间是A ,因此在operator< overload的参数依赖查找期间仅搜索A命名空间。

Charles Bailey的回答很好地解释了为什么在包含<set>时只能看到问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM