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

use*_*522 6 c++ std language-lawyer name-lookup

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");
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

ICC 总是能成功。


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


这里的根本问题是std::less使用<内部,因为它在模板中使用,它将operator<通过从实例化点(这是正确的方法)进行参数相关的查找来查找重载,而且还通过从定义点进行非限定名称查找来查找重载模板。

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

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

Dav*_*ing 2

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

\n