简体   繁体   English

为什么在 C++20 之前 std::swap 没有标记为 constexpr ?

[英]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::swapstd::invoke都必须依赖于检查成员函数(前者中的移动构造/赋值和后者中的调用运算符/代理调用),因此它们都取决于此问题的解决方案。

The reason原因

(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 中解决了语言缺陷,您无法检查它,因为就编译器而言,相关成员函数可能尚未定义。

The chronology年表

  • 2016: Antony Polukhin submitts proposal P0202 , to mark all of the <algorithm> functions as constexpr . 2016 年:Antony Polukhin 提交提案P0202 ,将所有<algorithm>函数标记为constexpr
  • The core working group of the standard committee discusses defect CWG-1581 .标准委员会核心工作组讨论缺陷CWG-1581 This issue made it problematic to have constexpr std::swap() and also constexpr std::invoke() - see explanation above.这个问题使得constexpr std::swap()constexpr std::invoke() - 请参阅上面的解释。
  • 2017: Antony revises his proposal a few times to exclude std::swap and some other constructs, and this is accepted into C++17. 2017 年:安东尼修改了他的提议几次以排除std::swap和其他一些结构,这被 C++17 接受。
  • 2017: A resolution for CWG-1581 issue is submitted as P0859 and accepted by the standard committee in 2017 (but after C++17 shipped). 2017 年:CWG-1581 问题的决议作为P0859提交,并于 2017 年被标准委员会接受(但在 C++17 发布之后)。
  • End of 2017: Antony submits a complementary proposal, P0879 , to make std::swap() constexpr after the resolution of CWG-1581. 2017 年底:安东尼提交补充提案P0879 ,在 CWG-1581 决议后使std::swap() constexpr 。
  • 2018: The complementary proposal is accepted (?) into C++20. 2018 年:补充提案被接受(?)到 C++20 中。 As Barry points out, so is the constexpr std::invoke() fix.正如巴里指出的那样, constexpr std::invoke()修复也是如此。

Your specific case您的具体情况

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM