[英]Why isn't std::swap marked constexpr before C++20?
In C++20, std::swap
becomes a constexpr
function.在 C++20 中,
std::swap
变成了constexpr
函数。
I know that the standard library really lagged behind the language in marking things constexpr
, but by 2017, <algorithm>
was pretty much constexpr as were a bunch of other things.我知道标准库在标记事物
constexpr
方面确实落后于语言,但到 2017 年, <algorithm>
和其他一些东西一样,几乎都是 constexpr。 Yet - std::swap
wasn't.然而 -
std::swap
不是。 I vaguely remember there being some strange language defect which prevent that marking, but I forget the details.我依稀记得有一些奇怪的语言缺陷阻止了那个标记,但我忘记了细节。
Can someone explain this succinctly and clearly?有人可以简洁明了地解释这一点吗?
Motivation: Need to understand why it may be bad idea to mark an std::swap()
-like function constexpr
in C++11/C++14 code.动机:需要理解为什么在 C++11/C++14 代码中标记一个
std::swap()
的函数constexpr
可能是个坏主意。
The strange language issue is CWG 1581 :奇怪的语言问题是CWG 1581 :
Clause 15 [special] is perfectly clear that special member functions are only implicitly defined when they are odr-used.
第 15 条 [特殊] 非常清楚,特殊成员函数仅在 odr 使用时才隐式定义。 This creates a problem for constant expressions in unevaluated contexts:
这给未计算上下文中的常量表达式带来了问题:
struct duration { constexpr duration() {} constexpr operator int() const { return 0; } }; // duration d = duration(); // #1 int n = sizeof(short{duration(duration())});
The issue here is that we are not permitted to implicitly define
constexpr duration::duration(duration&&)
in this program, so the expression in the initializer list is not a constant expression (because it invokes a constexpr function which has not been defined), so the braced initializer contains a narrowing conversion, so the program is ill-formed.这里的问题是我们不允许在这个程序中隐式定义
constexpr duration::duration(duration&&)
,所以初始化列表中的表达式不是一个常量表达式(因为它调用了一个尚未定义的 constexpr 函数),所以括号初始化器包含一个收缩转换,所以程序是格式错误的。If we uncomment line #1, the move constructor is implicitly defined and the program is valid.
如果我们取消注释第 1 行,则隐式定义了移动构造函数并且程序有效。 This spooky action at a distance is extremely unfortunate.
这种远距离的诡异动作是极其不幸的。 Implementations diverge on this point.
在这一点上,实现存在分歧。
You can read the rest of the issue description.您可以阅读问题描述的其余部分。
A resolution for this issue was adopted in P0859 in Albuquerque in 2017 (after C++17 shipped). 2017 年在阿尔伯克基的P0859中(在 C++17 发布后)通过了针对此问题的解决方案。 That issue was a blocker for both being able to have a
constexpr std::swap
(resolved in P0879 ) and a constexpr std::invoke
(resolved in P1065 , which also has CWG1581 examples), both for C++20.这个问题是一个阻碍,因为它们都能够有一个
constexpr std::swap
(在P0879 中解决)和一个constexpr std::invoke
(在P1065 中解决,它也有 CWG1581 示例),两者都适用于 C++20。
The simplest to understand example here, in my opinion, is the code from the LLVM bug report pointed out in P1065:在我看来,这里最容易理解的示例是 P1065 中指出的 LLVM 错误报告中的代码:
template<typename T> int f(T x) { return x.get(); } template<typename T> constexpr int g(T x) { return x.get(); } int main() { // OK The body of `f' is not required. decltype(f(0)) a; // Seems to instantiate the body of `g' // and results in an error. decltype(g(0)) b; return 0; }
CWG1581 is all about when constexpr member functions are defined, and the resolution ensures that they're only defined when used. CWG1581 是关于何时定义 constexpr 成员函数的,并且决议确保它们仅在使用时被定义。 After P0859, the above is well-formed (the type of
b
is int
).在 P0859 之后,以上是格式良好的(
b
的类型是int
)。
Since std::swap
and std::invoke
both have to rely upon checking for member functions (move construction/assignment in the former and the call operator/surrogate calls in the latter), they both depended on the resolution of this issue.由于
std::swap
和std::invoke
都必须依赖于检查成员函数(前者中的移动构造/赋值和后者中的调用运算符/代理调用),因此它们都取决于此问题的解决方案。
(due to @NathanOliver) (由于@NathanOliver)
To allow a constexpr
swap function, you have to check - before instantiating the template for this function - that the swapped type is move-constructible and move-assignable.要允许
constexpr
交换函数,您必须检查 - 在实例化此函数的模板之前 - 交换的类型是可移动构造和可移动分配的。 Unfortunately, due to a language defect only resolved in C++20, you can't check for that, since the relevant member functions may not have been defined yet, as far as the compiler is concerned.不幸的是,由于仅在 C++20 中解决了语言缺陷,您无法检查它,因为就编译器而言,相关成员函数可能尚未定义。
<algorithm>
functions as constexpr
. <algorithm>
函数标记为constexpr
。constexpr std::swap()
and also constexpr std::invoke()
- see explanation above.constexpr std::swap()
和constexpr std::invoke()
- 请参阅上面的解释。std::swap
and some other constructs, and this is accepted into C++17. std::swap
和其他一些结构,这被 C++17 接受。std::swap()
constexpr after the resolution of CWG-1581. std::swap()
constexpr 。std::invoke()
fix.std::invoke()
修复也是如此。You can use constexpr
swapping if you don't check for move-constructibility and move-assignability, but rather directly check for some other feature of types which ensures that in particular.如果您不检查移动可构造性和移动可分配性,而是直接检查某些其他类型的特性,特别是确保这一点,则可以使用
constexpr
交换。 eg only primitive types and no classes or structs.例如,只有原始类型,没有类或结构。 Or, theoretically, you could forego the checks and just deal with any compilation errors you might encounter, and with flaky behavior switching between compilers.
或者,理论上,您可以放弃检查并只处理您可能遇到的任何编译错误,以及编译器之间的不稳定行为切换。 In any case, don't replace
std::swap()
with that kind of a thing.无论如何,不要用那种东西替换
std::swap()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.