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


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上失败。除了版本13之外,它在所有版本中都成功地使用了libc++,包括当前的trunk。

它总是在国际商会取得成功。


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


这里的根本问题是std::less在内部使用<,因为它在模板中使用,所以从实例化的角度(这是正确的方法(,通过参数相关查找会发现operator<重载,但从模板定义的角度,也会通过非限定名称查找。

如果找到全局过载,则匹配效果更好。不幸的是,这使得程序行为依赖于标准库包含的位置和顺序。

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

这里重要的是来自std内部的非限定查找在到达全局命名空间之前是否找到任何其他operator<(无论其签名如何!(。这取决于包含了什么标头(任何标准库标头都可能包含任何其他标头(,也取决于语言版本,因为C++20operator<=>替换了许多这样的运算符。此外,偶尔这样的东西会被重新指定为隐藏的朋友,这些朋友不是通过无条件查找找到的。无论如何,依赖它显然是不明智的。

最新更新