标准库模板中运算符的不合格查找
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这里重要的是,在到达全局命名空间之前,来自
std
内部的非限定查找是否找到任何其他运算符<
(无论其签名如何!)。这取决于包含哪些标头(任何标准库标头都可能包含任何其他标头),并且还取决于语言版本,因为 C++20 取代了许多此类运算符与运算符<=>
。此外,有时此类事物会被重新指定为通过不合格查找找不到的隐藏好友。无论如何依赖它显然是不明智的。What matters here is whether the unqualified lookup from inside
std
finds any otheroperator<
(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 withoperator<=>
. 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.