简体   繁体   English

关于std :: less行为的问题

[英]Question about std::less behavior

What is happening there? 那里发生了什么?

#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;
}

This is compiled ok. 编译好了。 But if I use. 但是,如果我使用。

#include <set>

I got errors: 我有错误:

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

The reason that the lookup fails is that set introduces an operator< for std::set in the std namespace that hides all other global operator< . 该查找失败的原因是set引入了operator<std::setstd隐藏所有其他的全局命名空间operator<

The lookup of < in the instantiation of std::less happens inside a scope inside the std namespace. std::less的实例化中查找<std命名空间内的范围内发生。 The only way that any operator< outside of std namespace will become visible is if ADL kicks into action and this only happens for the nearest enclosing namespace. 任何operator< std命名空间之外的唯一方法是ADL开始行动,这只发生在最近的封闭命名空间中。

Without including <set> , there is no operator< introduced (and this is probably implementation dependent) in the std namespace that hides the global operator< and hence the non-ADL lookup rules can still find the global operator< which take A::Class . 在不包含<set>std命名空间中没有operator<引入(这可能是依赖于实现的)隐藏了全局operator<因此非ADL查找规则仍然可以找到全局operator<哪个采用A::Class

Correction: As @JohannesSchaub points out, this analysis would only be correct if the declaration of operator< occurred before <functional> (where std::less is defined) was first included. 更正:正如@JohannesSchaub指出的那样,只有在第一次包含<functional> (其中定义了std::less )之前发生了operator<的声明时,此分析才是正确的。 As it is, the only overloads of a function called through an unqualified-id that should be visible to non-ADL lookup inside a template definition are those visible at the point of definition. 实际上,在模板定义中非ADL查找应该可见的非限定id调用的函数的唯一重载是在定义点可见的那些重载。 Definitions introduced between the point of definition and the point of instantiation should only be visible for ADL lookup. 在定义点和实例化点之间引入的定义应仅对ADL查找可见。 (In an expression such as x < y candidate functions named operator< are searched for and this is one particular form of unqualified-id .) (在一个表达式中,例如x < y候选函数名为operator<被搜索,这是unqualified-id的一种特殊形式。)

As it stands, the overloaded operator< should not be considered a candidate with or without the <set> include, although these corner cases in the lookup rules are not always handled correctly by all compilers. 就目前而言,重载operator<不应被视为具有或不具有<set> include的候选者,尽管查找规则中的这些极端情况并非总是由所有编译器正确处理。

That operator< should be in namespace A as well, otherwise it cannot be looked up. operator<应该在namespace A中,否则无法查找。

Details: First of all, simply calling operator< on two Class objects from main() , or even implementing your own less of course works whether operator< is in the same namespace as Class or not, although it should be in the same namespace since that's what everyone, including the implementors of the libraries, expects. 详细信息:首先,简单地从main()调用operator< on两个Class对象,或者甚至实现自己的less ,无论operator<是否与Class在同一个命名空间中都有效,尽管它应该在同一个命名空间中这就是每个人,包括图书馆的实施者所期望的。 Both gcc and MSVC 2010 (as I just tested) include several additional operator< 's in their standard libraries, between which the compiler fails to resolve. gcc和MSVC 2010(我刚刚测试过)都在其标准库中包含了几个额外的operator< ,编译器之间无法解析。

gcc 4.5.2's error message is misleading. gcc 4.5.2的错误消息具有误导性。 Compiling the same with a pre-release 4.6, I get the more specific 使用预发行版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>&)

Incidentally, SunCC (with both rw and stlport4) compiles this cleanly and even creates a usable std::set<A::Class> . 顺便说一下,SunCC(同时使用rw和stlport4)可以干净地编译它,甚至可以创建一个可用的std::set<A::Class> <A :: Class>。

Your operator< overload needs to be in the same namespace as Class (that is, it also needs to be in the A namespace). 您的operator< overload需要与Class位于同一名称空间中(也就是说,它也需要位于A名称空间中)。

C++ has complex name lookup rules. C ++具有复杂的名称查找规则。 One of the more interesting "features" is "argument-dependent lookup," or ADL, where names can be looked up in namespaces associated with a function's arguments. 其中一个更有趣的“功能”是“依赖于参数的查找”或ADL,其中名称可以在与函数参数关联的名称空间中查找。 For a class that has no base classes and isn't a nested class, like your Class , the only associated namespace is the namespace within which the class is located. 对于没有基类且不是嵌套类的类(如Class ,唯一关联的命名空间是类所在的命名空间。

So, the only namespace associated with Class is A , so only the A namespace is searched during argument-dependent lookup for an operator< overload. 因此,与Class关联的唯一命名空间是A ,因此在operator< overload的参数依赖查找期间仅搜索A命名空间。

Charles Bailey's answer provides a good explanation of why you only see the problem when including <set> . Charles Bailey的回答很好地解释了为什么在包含<set>时只能看到问题。

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

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