繁体   English   中英

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

[英]Why isn't std::swap marked constexpr before C++20?

在 C++20 中, std::swap变成了constexpr函数。

我知道标准库在标记事物constexpr方面确实落后于语言,但到 2017 年, <algorithm>和其他一些东西一样,几乎都是 constexpr。 然而 - std::swap不是。 我依稀记得有一些奇怪的语言缺陷阻止了那个标记,但我忘记了细节。

有人可以简洁明了地解释这一点吗?

动机:需要理解为什么在 C++11/C++14 代码中标记一个std::swap()的函数constexpr可能是个坏主意。

奇怪的语言问题是CWG 1581

第 15 条 [特殊] 非常清楚,特殊成员函数仅在 odr 使用时才隐式定义。 这给未计算上下文中的常量表达式带来了问题:

 struct duration { constexpr duration() {} constexpr operator int() const { return 0; } }; // duration d = duration(); // #1 int n = sizeof(short{duration(duration())});

这里的问题是我们不允许在这个程序中隐式定义constexpr duration::duration(duration&&) ,所以初始化列表中的表达式不是一个常量表达式(因为它调用了一个尚未定义的 constexpr 函数),所以括号初始化器包含一个收缩转换,所以程序是格式错误的。

如果我们取消注释第 1 行,则隐式定义了移动构造函数并且程序有效。 这种远距离的诡异动作是极其不幸的。 在这一点上,实现存在分歧。

您可以阅读问题描述的其余部分。

2017 年在阿尔伯克基的P0859中(在 C++17 发布后)通过了针对此问题的解决方案。 这个问题是一个阻碍,因为它们都能够有一个constexpr std::swap (在P0879 中解决)和一个constexpr std::invoke (在P1065 中解决,它也有 CWG1581 示例),两者都适用于 C++20。


在我看来,这里最容易理解的示例是 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 是关于何时定义 constexpr 成员函数的,并且决议确保它们仅在使用时被定义。 在 P0859 之后,以上是格式良好的( b的类型是int )。

由于std::swapstd::invoke都必须依赖于检查成员函数(前者中的移动构造/赋值和后者中的调用运算符/代理调用),因此它们都取决于此问题的解决方案。

原因

(由于@NathanOliver)

要允许constexpr交换函数,您必须检查 - 在实例化此函数的模板之前 - 交换的类型是可移动构造和可移动分配的。 不幸的是,由于仅在 C++20 中解决了语言缺陷,您无法检查它,因为就编译器而言,相关成员函数可能尚未定义。

年表

  • 2016 年:Antony Polukhin 提交提案P0202 ,将所有<algorithm>函数标记为constexpr
  • 标准委员会核心工作组讨论缺陷CWG-1581 这个问题使得constexpr std::swap()constexpr std::invoke() - 请参阅上面的解释。
  • 2017 年:安东尼修改了他的提议几次以排除std::swap和其他一些结构,这被 C++17 接受。
  • 2017 年:CWG-1581 问题的决议作为P0859提交,并于 2017 年被标准委员会接受(但在 C++17 发布之后)。
  • 2017 年底:安东尼提交补充提案P0879 ,在 CWG-1581 决议后使std::swap() constexpr 。
  • 2018 年:补充提案被接受(?)到 C++20 中。 正如巴里指出的那样, constexpr std::invoke()修复也是如此。

您的具体情况

如果您检查移动可构造性和移动可分配性,而是直接检查某些其他类型的特性,特别是确保这一点,则可以使用constexpr交换。 例如,只有原始类型,没有类或结构。 或者,理论上,您可以放弃检查并只处理您可能遇到的任何编译错误,以及编译器之间的不稳定行为切换。 无论如何,不​​要用那种东西替换std::swap()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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