没有移动构造函数但带有接受772, 1173, 1614, 918, 666
参数的复制构造函数的类型满足const T&
。例如,在以下代码中:
std::is_move_constructible
#include <type_traits>
struct T {
T(const T&) {}
//T(T&&) = delete;
};
int main() {
static_assert(std::is_move_constructible<T>::value, "not move constructible");
return 0;
}
将没有隐式移动构造函数,因为它具有用户定义的复制构造函数。
但是,如果我们取消注释移动构造函数的显式删除,则代码不再编译。为什么是这样?我原以为显式拷贝构造函数仍然满足T
。
重载是否起作用,选择声明的移动构造函数然后失败,因为它被删除了?
如果标准规定了std::is_move_constructible
和no implicit move ctor
类之间的移动构造性之间的差异,请引用,并且如果可能的话,给出一个基本原理(例如&#34;提供设施)禁止移动建设&#34; - 首先想到的事情。)
答案 0 :(得分:16)
这是对我的第一个答案的彻底修改,纠正了一些错误,并从标准中引用了一些错误,并提出了提问者所希望的一些细节。
std::is_move_constructible
实际做什么
如果T
是结构,则std::is_move_constructible<T>
评估为std::is_constructible<T,T&&>
。如果std::is_constructible<T,U>
是T x(y)
类型y
的格式正确的表达式,则U
有效。因此,要使std::is_move_constructible<T>
成立,T x(std::move(y))
必须格式正确y
类型T
。
标准引用:
The predicate condition for a template specialization is_constructible<T, Args...>
shall be satisfied if and only if the following variable definition would
be well-formed for some invented variable t:
T t(create<Args>()...);
(...)
Template: template <class T> struct is_move_constructible;
Condition: For a referenceable type T, the same result as is_constructible<T, T&&>::value,
otherwise false.
Precondition: T shall be a complete type, (possibly cv-qualified) void,
or an array of unknown bound.
创建移动构造函数时
标准说,只有当用户没有声明复制构造函数,移动构造函数,赋值运算符或析构函数时,才会创建默认移动构造函数。
If the definition of a class X does not explicitly declare a move
constructor, one will be implicitly declared as defaulted if and only if
—X does not have a user-declared copy constructor,
—X does not have a user-declared copy assignment operator,
—X does not have a user-declared move assignment operator, and
—X does not have a user-declared destructor
但是,该标准允许您使用类rvalue初始化类左值引用。
Otherwise, the reference shall be an lvalue reference to a non-volatile const type
(i.e., cv1 shall be const), or the reference shall be an rvalue reference.
—If the initializer expression is an xvalue (but not a bit-field),
class prvalue, array prvalue or function lvalue and “cv1 T1”
is reference-compatible with “cv2 T2”, or (...)
then the reference is bound to the value of the initializer expression (...)
(or, in either case, to an appropriate base class subobject).
因此,如果您有一个类型T::T(S& other)
的副本构造函数y
和对象T&&
,即T
的右值引用,那么y
就是参考与T&
和T x(y)
兼容的是一个有效的表达式,用于调用复制构造函数T::T(S&)
。
示例结构的作用
让我继续您的第一个示例,删除const
关键字,以避免说明引用需要比初始化程序更多cv限定。
struct S {
S(S&) {}
};
让我们检查一下情况。由于存在用户定义的复制构造函数,因此没有隐式默认的移动构造函数。然而,
如果y
属于S
类型,则类型为std::move(y)
的{{1}}引用与类型S&&
兼容。因此S&
完全有效,并调用复制构造函数S x(std::move(y))
。
第二个例子做什么
S::S(const S&)
同样,没有定义移动构造函数,因为存在用户定义的复制构造函数和用户定义的移动构造函数。再次让struct T {
T(T&) {}
T(T&&) = delete;
};
属于y
类型并考虑T
。
但是,这次多个构造函数可以适合表达式,因此执行过载选择。尝试只使用最专业的匹配构造函数,因此只尝试调用移动构造函数T x(std::move(y))
。但移动构造函数已删除,因此无效。
<强>结论强>
第一个结构T::T(T&&)
可以使用其复制构造函数来执行类似移动的表达式,因为它是此表达式的最专业的构造函数。
第二个结构S
必须使用其显式声明的移动构造函数来执行类似移动的表达式,因为它是最专业的。但是,删除了该构造函数,move-construction表达式失败。
答案 1 :(得分:5)
移动可构造类是一个具有移动构造函数的类,无论是隐式的还是用户声明的。或者是为rvalue引用调用的复制构造函数。除非类具有已删除的移动构造函数,否则将引发这些构造函数。
因此:
struct T {
T(const T&) {}
};
是一个可移动的构造类,因此你可以从各自的特征中得到真实。
通过移动构造函数delete
声明:
struct T {
T(const T&) {}
T(T&&) = delete;
};
使你的班级不可移动。
即使移动构造函数是delete,它也会参与重载解析,就好像它不是delete一样。因此,首选直接匹配。
要使is_move_constructible
提供成员常量value
为真,变量定义T obj(T&&);
必须格式正确,如果删除T
的移动构造函数是格式错误的。因此,您获得的值等于false。
现在关于如何在编译时发生这种情况。寻找std::declval
的魔力。在某处使用std::declval
是特征的实现,以评估是否可以移动类对象而不实际评估表达式。
答案 2 :(得分:-1)
在我的测试中,答案似乎又错了。顺便说一句,我在MSVC 17和GNU 6.3上测试了以下代码:
struct T {
T(const T&) {}
};
static_assert(is_move_constructible<T>(), "");
T a;
T b(move(a)); // compiler error here
根据此处[http://en.cppreference.com/w/cpp/language/move_constructor],用户定义的复制构造函数将禁用隐式默认移动构造函数。代码无法编译。 但奇怪的是,元函数is_move_constructible&lt;&gt;是的,并且编译器抱怨错误发生在位置:T(b(move(a));
所以,在我看来,is_move_constructible的实现有一些缺陷。
我的错误,我在这里忘记了默认的构造函数。