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