关于 std::less 行为的问题

发布于 2024-10-16 08:40:34 字数 1029 浏览 2 评论 0原文

那里发生了什么?

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

这样编译就ok了。但如果我使用.

#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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萌逼全场 2024-10-23 08:40:34

查找失败的原因是 setstd 命名空间中为 std::set 引入了一个 operator<隐藏所有其他全局运算符

std::less 实例化中 < 的查找发生在 std 命名空间内的作用域内。 std 命名空间之外的任何operator< 变得可见的唯一方法是 ADL 启动,并且这种情况仅发生在最近的封闭命名空间中。

如果不包含 ,则在隐藏全局的 std 命名空间中不会引入 operator<(这可能与实现相关) operator<,因此非 ADL 查找规则仍然可以找到采用 A::Class 的全局 operator<

更正:正如 @JohannesSchaub 指出的,只有当 operator< 的声明发生在 之前(其中< code>std::less 被定义)首先被包含。实际上,通过 unqualified-id 调用的函数的唯一重载应该对模板定义内的非 ADL 查找可见,这些重载在定义时可见。在定义点和实例化点之间引入的定义应该仅对 ADL 查找可见。 (在诸如 x < y 之类的表达式中,会搜索名为 operator< 的候选函数,这是 unqualified-id 的一种特殊形式。 )

就目前情况而言,重载的 operator< 不应被视为带有或不带有 包含的候选者,尽管查找规则中的这些极端情况并不总是如此所有编译器都正确处理。

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<.

The lookup of < in the instantiation of std::less happens inside a scope inside the std namespace. 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.

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.

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. 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. Definitions introduced between the point of definition and the point of instantiation should only be visible for ADL lookup. (In an expression such as x < y candidate functions named operator< are searched for and this is one particular form of 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.

往日情怀 2024-10-23 08:40:34

操作符<也应该位于命名空间A中,否则无法查找。

细节:
首先,只需从 main() 调用两个 Class 对象上的 operator<,甚至实现您自己的 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

That operator< should be in namespace A as well, otherwise it cannot be looked up.

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. 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 4.5.2's error message is misleading. Compiling the same with a pre-release 4.6, I get the more specific

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>.

鱼忆七猫命九 2024-10-23 08:40:34

您的 operator< 重载需要与 Class 位于同一命名空间中(即,它也需要位于 A 命名空间中)。

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

因此,与 Class 关联的唯一命名空间是 A,因此在依赖于参数的运算符查找操作符时,仅搜索 A 命名空间A 命名空间; 过载。

Charles Bailey 的回答很好地解释了为什么您只在包含时才看到问题<代码><设置>。

Your operator< overload needs to be in the same namespace as Class (that is, it also needs to be in the A namespace).

C++ has complex name lookup rules. 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. 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.

So, the only namespace associated with Class is A, so only the A namespace is searched during argument-dependent lookup for an operator< overload.

Charles Bailey's answer provides a good explanation of why you only see the problem when including <set>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文