Class 似乎可以通过用户定义的析构函数移动
Class seems to be movable with user defined destructor
我想弄清楚为什么这段代码无法编译?
我已经创建了用户定义的析构函数,因此不应创建移动构造函数和移动赋值运算符。那为什么complication失败了,说我的class不满足要求?
#include <iostream>
#include <concepts>
#include <type_traits>
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget() = default;
};
int main()
{
auto x = Singleton<Widget>{};
}
编辑。这个版本也是一样。
#include <iostream>
#include <concepts>
#include <type_traits>
extern void fun();
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget()
{
fun();
}
};
int main()
{
auto x = Singleton<Widget>{};
}
错误信息
:23:28: note: constraints not satisfied
: In substitution of 'template requires !(movable) > struct Singleton [with T = Widget]':
:23:28: required from here
:8:8: required by the constraints of 'template requires !(movable) struct Singleton'
:7:30: note: the expression '!(movable) [with T = Widget]' evaluated to 'false'
7 | template requires (!std::movable)
在 godbolt 上使用 gcc (trunk) 编译。我使用的唯一编译标志是 -std=c++20
两个例子中都没有移动构造函数。但这不是 std::movable
检查的内容。它将您测试的检查推迟到 std::move_constructible
。该概念仅检查对象是否可以从右值直接初始化和复制初始化。
从 C++98 开始,复制构造函数就可以从右值进行初始化。它仍然适用于添加移动操作。这就是为什么该特征得到满足的原因。 Widget a; Widget b = std::move(a);
仍然有效,即使没有移动构造函数,因为编译器提供的复制构造函数使这成为可能。
编译器不生成移动构造函数是一项设计决策,目的是在迁移到 C++11 时保持旧代码正常工作。这样的代码会遵守三规则,因此将旧副本换成编译器生成的移动被认为是有风险的。但是旧代码仍然可以从右值初始化对象,所以复制构造函数保留了它的旧功能。
值得注意的是,该特性在未来可能会发挥预期的作用。此处定义的默认复制构造函数是已弃用的功能:
[class.copy.ctor]
6 If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted ([dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor ([depr.impldec]).
我想弄清楚为什么这段代码无法编译? 我已经创建了用户定义的析构函数,因此不应创建移动构造函数和移动赋值运算符。那为什么complication失败了,说我的class不满足要求?
#include <iostream>
#include <concepts>
#include <type_traits>
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget() = default;
};
int main()
{
auto x = Singleton<Widget>{};
}
编辑。这个版本也是一样。
#include <iostream>
#include <concepts>
#include <type_traits>
extern void fun();
template<class T> requires (!std::movable<T>)
struct Singleton
{
};
struct Widget
{
~Widget()
{
fun();
}
};
int main()
{
auto x = Singleton<Widget>{};
}
错误信息
:23:28: note: constraints not satisfied : In substitution of 'template requires !(movable) > struct Singleton [with T = Widget]': :23:28: required from here :8:8: required by the constraints of 'template requires !(movable) struct Singleton' :7:30: note: the expression '!(movable) [with T = Widget]' evaluated to 'false' 7 | template requires (!std::movable)
在 godbolt 上使用 gcc (trunk) 编译。我使用的唯一编译标志是 -std=c++20
两个例子中都没有移动构造函数。但这不是 std::movable
检查的内容。它将您测试的检查推迟到 std::move_constructible
。该概念仅检查对象是否可以从右值直接初始化和复制初始化。
从 C++98 开始,复制构造函数就可以从右值进行初始化。它仍然适用于添加移动操作。这就是为什么该特征得到满足的原因。 Widget a; Widget b = std::move(a);
仍然有效,即使没有移动构造函数,因为编译器提供的复制构造函数使这成为可能。
编译器不生成移动构造函数是一项设计决策,目的是在迁移到 C++11 时保持旧代码正常工作。这样的代码会遵守三规则,因此将旧副本换成编译器生成的移动被认为是有风险的。但是旧代码仍然可以从右值初始化对象,所以复制构造函数保留了它的旧功能。
值得注意的是,该特性在未来可能会发挥预期的作用。此处定义的默认复制构造函数是已弃用的功能:
[class.copy.ctor]
6 If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted ([dcl.fct.def]). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor ([depr.impldec]).