不可能的隐式移动操作?
Impossible implicit move operations?
据我了解[class.copy.ctor] and [class.copy.assign],以下代码中的结构A
不应该是可移动构造的,也不是可移动赋值的:
#include <type_traits>
struct X {
X() noexcept; // user-declared default constructor
~X() noexcept; // Force X not to be trivially copyable
X(X &&) = delete; // Explicitly deleted move constructor
X(X const &) = delete; // Explicitly deleted copy constructor
X & operator=(X &&) = delete; // Explicitly deleted move assignment operator
X & operator=(X const &) = delete; // Explicitly deleted copy assignment op.
};
static_assert(!std::is_copy_constructible<X>::value, "");
static_assert(!std::is_copy_assignable<X>::value, "");
static_assert(!std::is_move_assignable<X>::value, "");
static_assert(!std::is_move_constructible<X>::value, "");
static_assert(!std::is_trivially_copyable<X>::value, "");
static_assert(!std::is_trivially_copy_assignable<X>::value, "");
static_assert(!std::is_trivially_copy_constructible<X>::value, "");
static_assert(!std::is_trivially_move_assignable<X>::value, "");
static_assert(!std::is_trivially_move_constructible<X>::value, "");
struct A {
A() noexcept; // user-declared default constructor
A(A const &) noexcept; // user-declared copy constructor
A & operator=(A const &) noexcept; // user-declared copy assignment operator
X x;
};
static_assert(std::is_copy_constructible<A>::value, "");
static_assert(std::is_copy_assignable<A>::value, "");
static_assert(!std::is_move_assignable<A>::value, ""); // FAILS?!
static_assert(!std::is_move_constructible<A>::value, ""); // FAILS?!
static_assert(!std::is_trivially_copyable<A>::value, "");
static_assert(!std::is_trivially_copy_assignable<A>::value, "");
static_assert(!std::is_trivially_copy_constructible<A>::value, "");
static_assert(!std::is_trivially_move_assignable<A>::value, "");
static_assert(!std::is_trivially_move_constructible<A>::value, "");
但是,有两个静态断言在 GCC 和 Clang 中都失败了,这意味着由于某种原因 A
是可移动赋值和可移动构造的。
在我看来这不应该,因为 struct A
:
- 没有显式声明移动构造函数
- 没有显式声明移动赋值运算符
- 有一个用户声明的复制构造函数
- 有一个用户声明的复制赋值运算符。
- 有一个类型为
X
的字段 x
,它不能用任何其他 A::x
直接初始化,因为 X
的所有构造函数都将参与为此的重载决议已明确删除。
这是编译器错误还是我误解了什么?
它们是预期的行为,因为复制构造函数和复制赋值运算符的存在满足 MoveConstructible
的要求
A class does not have to implement a move constructor to satisfy this type requirement: a copy constructor that takes a const T& argument can bind rvalue expressions.
The type does not have to implement move assignment operator in order to satisfy this type requirement: a copy assignment operator that takes its parameter by value or as a const Type&, will bind to rvalue argument.
请注意,std::is_move_constructible and std::is_move_assignable 只是检查指定类型的对象是否可以 constructed/assigned 来自右值参数。即使没有移动 constructor/assignment 运算符,复制 constructor/assignment 运算符也可以完成这项工作,因为右值参数也可以传递给对 const 的左值引用。
编辑
请注意您显示的示例代码 constructor/assignment 运算符根本没有隐式声明(因为 user-declared 复制构造函数和复制赋值运算符的存在),所以它们不会影响重载决策和调用 copy constructor/assignment 运算符的结果。但是如果你显式声明它们为delete
,行为会改变,因为显式删除的函数参与重载决策,它们将被优先选择,然后std::is_move_constructible
和std::is_move_assignable
将return false
.
is_move_constructible/assignable
不检查是否有移动构造函数或移动赋值运算符。它检查 class 是否可以是来自 r-value 参考的 constructed/assigned。 类 在这种情况下,缺少移动设备将只使用复制 constructor/assignment 运算符。
通常 is_move_constructible/assignable
比 is_copy_constructible/assignable
弱:当前者通过检查时,move-only 类型稍后会失败。
据我了解[class.copy.ctor] and [class.copy.assign],以下代码中的结构A
不应该是可移动构造的,也不是可移动赋值的:
#include <type_traits>
struct X {
X() noexcept; // user-declared default constructor
~X() noexcept; // Force X not to be trivially copyable
X(X &&) = delete; // Explicitly deleted move constructor
X(X const &) = delete; // Explicitly deleted copy constructor
X & operator=(X &&) = delete; // Explicitly deleted move assignment operator
X & operator=(X const &) = delete; // Explicitly deleted copy assignment op.
};
static_assert(!std::is_copy_constructible<X>::value, "");
static_assert(!std::is_copy_assignable<X>::value, "");
static_assert(!std::is_move_assignable<X>::value, "");
static_assert(!std::is_move_constructible<X>::value, "");
static_assert(!std::is_trivially_copyable<X>::value, "");
static_assert(!std::is_trivially_copy_assignable<X>::value, "");
static_assert(!std::is_trivially_copy_constructible<X>::value, "");
static_assert(!std::is_trivially_move_assignable<X>::value, "");
static_assert(!std::is_trivially_move_constructible<X>::value, "");
struct A {
A() noexcept; // user-declared default constructor
A(A const &) noexcept; // user-declared copy constructor
A & operator=(A const &) noexcept; // user-declared copy assignment operator
X x;
};
static_assert(std::is_copy_constructible<A>::value, "");
static_assert(std::is_copy_assignable<A>::value, "");
static_assert(!std::is_move_assignable<A>::value, ""); // FAILS?!
static_assert(!std::is_move_constructible<A>::value, ""); // FAILS?!
static_assert(!std::is_trivially_copyable<A>::value, "");
static_assert(!std::is_trivially_copy_assignable<A>::value, "");
static_assert(!std::is_trivially_copy_constructible<A>::value, "");
static_assert(!std::is_trivially_move_assignable<A>::value, "");
static_assert(!std::is_trivially_move_constructible<A>::value, "");
但是,有两个静态断言在 GCC 和 Clang 中都失败了,这意味着由于某种原因 A
是可移动赋值和可移动构造的。
在我看来这不应该,因为 struct A
:
- 没有显式声明移动构造函数
- 没有显式声明移动赋值运算符
- 有一个用户声明的复制构造函数
- 有一个用户声明的复制赋值运算符。
- 有一个类型为
X
的字段x
,它不能用任何其他A::x
直接初始化,因为X
的所有构造函数都将参与为此的重载决议已明确删除。
这是编译器错误还是我误解了什么?
它们是预期的行为,因为复制构造函数和复制赋值运算符的存在满足 MoveConstructible
的要求A class does not have to implement a move constructor to satisfy this type requirement: a copy constructor that takes a const T& argument can bind rvalue expressions.
The type does not have to implement move assignment operator in order to satisfy this type requirement: a copy assignment operator that takes its parameter by value or as a const Type&, will bind to rvalue argument.
请注意,std::is_move_constructible and std::is_move_assignable 只是检查指定类型的对象是否可以 constructed/assigned 来自右值参数。即使没有移动 constructor/assignment 运算符,复制 constructor/assignment 运算符也可以完成这项工作,因为右值参数也可以传递给对 const 的左值引用。
编辑
请注意您显示的示例代码 constructor/assignment 运算符根本没有隐式声明(因为 user-declared 复制构造函数和复制赋值运算符的存在),所以它们不会影响重载决策和调用 copy constructor/assignment 运算符的结果。但是如果你显式声明它们为delete
,行为会改变,因为显式删除的函数参与重载决策,它们将被优先选择,然后std::is_move_constructible
和std::is_move_assignable
将return false
.
is_move_constructible/assignable
不检查是否有移动构造函数或移动赋值运算符。它检查 class 是否可以是来自 r-value 参考的 constructed/assigned。 类 在这种情况下,缺少移动设备将只使用复制 constructor/assignment 运算符。
通常 is_move_constructible/assignable
比 is_copy_constructible/assignable
弱:当前者通过检查时,move-only 类型稍后会失败。