在 C++20 中,std::swap
变成 constexpr
功能。
我知道标准库在标记事物方面确实落后于语言constexpr
,但到 2017 年,<algorithm>
和其他一堆东西一样,几乎都是 constexpr。然而 - std::swap
不是。我依稀记得有一些奇怪的语言缺陷阻止了那个标记,但我忘记了细节。
有人可以简洁明了地解释这一点吗?
动机:需要理解为什么标记 std::swap()
可能是个坏主意类函数constexpr
在 C++11/C++14 代码中。
最佳答案
奇怪的语言问题是CWG 1581 :
Clause 15 [special] is perfectly clear that special member functions are only implicitly defined when they are odr-used. 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.If we uncomment line #1, the move constructor is implicitly defined and the program is valid. This spooky action at a distance is extremely unfortunate. Implementations diverge on this point.
您可以阅读问题描述的其余部分。
在 P0859 中通过了针对此问题的解决方案2017 年在阿尔伯克基(在 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() { // 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 之后,以上是格式良好的(
b
的类型是 int
)。自
std::swap
和 std::invoke
两者都必须依赖于检查成员函数(前者中的移动构造/赋值,后者中的调用运算符/代理调用),它们都取决于此问题的解决方案。
关于c++ - 为什么在 C++20 之前 std::swap 没有标记为 constexpr ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60674939/