ein*_*ica 14 c++ constexpr c++11 c++20
在 C++20 中,std::swap变成constexpr函数。
我知道标准库在标记事物方面确实落后于语言constexpr,但到 2017 年,<algorithm>它和其他一些东西几乎都是 constexpr。然而 -std::swap不是。我依稀记得有一些奇怪的语言缺陷阻止了那个标记,但我忘记了细节。
有人可以简洁明了地解释这一点吗?
动机:需要理解为什么在 C++11/C++14 代码中标记一个std::swap()-like 函数可能是个坏主意constexpr。
Bar*_*rry 11
奇怪的语言问题是CWG 1581:
第 15 条 [特殊] 非常清楚,特殊成员函数仅在 odr 使用时才隐式定义。这给未计算上下文中的常量表达式带来了问题:
Run Code Online (Sandbox Code Playgroud)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 错误报告中的代码:
Run Code Online (Sandbox Code Playgroud)template<typename T> int f(T x) { return x.get(); } template<typename T> constexpr int g(T x) { return x.get(); } int main() { // O.K. 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之后,上面的就是well-formed(类型b是int)。
由于std::swap和std::invoke两者都必须依赖于检查成员函数(前者中的移动构造/赋值和后者中的调用运算符/代理调用),因此它们都依赖于这个问题的解决方案。
(由于@NathanOliver)
要允许constexpr交换函数,您必须检查 - 在实例化此函数的模板之前 - 交换的类型是可移动构造的和可移动分配的。不幸的是,由于仅在 C++20 中解决的语言缺陷,您不能检查它,因为就编译器而言,相关成员函数可能尚未定义。
<algorithm>功能标记为constexpr.constexpr std::swap()并且constexpr std::invoke()- 请参阅上面的解释。std::swap和其他一些结构,这被 C++17 接受。std::swap()在 CWG-1581 决议后制作constexpr。std::invoke()修复也是如此。constexpr如果您不检查移动可构造性和移动可分配性,而是直接检查某些其他类型的特性,特别是确保这一点,则可以使用交换。例如,只有原始类型,没有类或结构。或者,理论上,您可以放弃检查并只处理您可能遇到的任何编译错误,以及编译器之间的不稳定行为切换。无论如何,不要std::swap()用那种东西代替。
| 归档时间: | 
 | 
| 查看次数: | 703 次 | 
| 最近记录: |