标准库模板中运算符的不合格查找

发布于 2025-01-12 01:43:39 字数 1045 浏览 2 评论 0原文

namespace N {
    struct A {};
    
    template<typename T>
    constexpr bool operator<(const T&, const T&) { return true; }
}

constexpr bool operator<(const N::A&, const N::A&) { return false; }

#include<functional>

int main() {
    static_assert(std::less<N::A>{}({}, {}), "assertion failed");
}

请参阅https://godbolt.org/z/vsd3qfch6

该程序在看似随机的编译器版本上进行编译。

自 v19.15 以来,断言在所有版本的 MSVC 上都失败,但在 v19.14 上成功。

它在 GCC 11.2 及之前版本上成功,但在当前 GCC 主干上失败。

在所有版本中,它在带有 libstdc++ 的 Clang 上都会失败。它在所有版本中的 libc++ 上都成功,包括当前的主干,版本 13 除外。

它在 ICC 上总是成功。


是否指定static_assert是否应该成功?


这里的根本问题是 std::less 在内部使用 < ,因为它在模板中使用,所以会通过以下方式找到 operator< 重载:从实例化点进行依赖于参数的查找(这是正确的方法),还可以从模板定义点进行非限定名称查找。

如果发现全局重载,则这是一个更好的匹配。不幸的是,这使得程序行为依赖于标准库包含的位置和顺序。

我本来期望标准库禁用 std 命名空间之外的非限定名称查找,因为无论如何都不能依赖它,但这应该得到保证吗?

namespace N {
    struct A {};
    
    template<typename T>
    constexpr bool operator<(const T&, const T&) { return true; }
}

constexpr bool operator<(const N::A&, const N::A&) { return false; }

#include<functional>

int main() {
    static_assert(std::less<N::A>{}({}, {}), "assertion failed");
}

See https://godbolt.org/z/vsd3qfch6.

This program compiles on seemingly random versions of compilers.

The assertion fails on all versions of MSVC since v19.15, but succeeds on v19.14.

It succeeds on GCC 11.2 and before, but fails on current GCC trunk.

It fails on Clang with libstdc++ in all versions. It succeeds with libc++ in all versions, including current trunk, except version 13.

It always succeeds with ICC.


Is it specified whether or not the static_assert should succeed?


The underlying issue here is that std::less uses < internally, which because it is used in a template will find operator< overloads via argument-dependent lookup from the point of instantiation (which is the proper method), but also via unqualified name lookup from the point of definition of the template.

If the global overload is found, it is a better match. Unfortunately this make the program behavior dependent on the placement and order of the standard library includes.

I would have expected that the standard library disables unqualified name lookup outside the std namespace, since it cannot be relied on anyway, but is that supposed to be guaranteed?

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

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

发布评论

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

评论(1

謌踐踏愛綪 2025-01-19 01:43:39

这里重要的是,在到达全局命名空间之前,来自 std 内部的非限定查找是否找到任何其他运算符<(无论其签名如何!)。这取决于包含哪些标头(任何标准库标头都可能包含任何其他标头),并且还取决于语言版本,因为 C++20 取代了许多此类运算符与运算符<=>。此外,有时此类事物会被重新指定为通过不合格查找找不到的隐藏好友。无论如何依赖它显然是不明智的。

What matters here is whether the unqualified lookup from inside std finds any other operator< (regardless of its signature!) before reaching the global namespace. That depends on what headers have been included (any standard library header may include any other), and it also depends on the language version since C++20 replaced many such operators with operator<=>. Also, occasionally such things are respecified as hidden friends that are not found by unqualified lookup. It’s obviously unwise to rely on it in any case.

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