Answers:
奇怪的语言问题是CWG 1581:
第15条[special]非常清楚,特殊成员函数仅在使用过时才隐式定义。这给未评估的上下文中的常量表达式带来了问题:
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行,则将隐式定义move构造函数,并且该程序有效。远距离的这种怪异的动作是非常不幸的。在这一点上,实现方式有所不同。
您可以阅读问题说明的其余部分。
2017年在Albuquerque的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() { // 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
两者都必须依靠检查成员功能(在前者中移动构造/分配,在后者中移动呼叫操作者/代理呼叫),因此它们都依赖于此问题的解决方案。
std::is_move_constructible_v<T> && std::is_move_assignable_v<T>
为true
。如果尚未生成特殊成员函数,则不会发生这种情况。
(由于@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()
用那种东西代替。