c++ - 不可能的隐式 move 操作?

标签 c++ c++11 language-lawyer move-semantics compiler-bug

据我了解[class.copy.ctor][class.copy.assign] , 以下代码中的结构 A 不应是可 move 构造的或可 move 赋值的:

#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 是可 move 赋值和可 move 构造的。

在我看来这不应该,因为struct A:

  • 没有显式声明 move 构造函数
  • 没有显式声明 move 赋值运算符
  • 有一个用户声明的拷贝构造函数
  • 具有用户声明的复制赋值运算符。
  • 有一个 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.

MoveAssignable .

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_constructiblestd::is_move_assignable只是检查指定类型的对象是否可以从右值参数构造/分配。即使没有 move 构造函数/赋值运算符,复制构造函数/赋值运算符也可以完成这项工作,因为右值参数也可以传递给对 const 的左值引用。

编辑

请注意,您展示的示例代码中, move 构造函数/赋值运算符根本没有隐式声明(因为存在用户声明的复制构造函数和复制赋值运算符),因此它们不会影响重载决策和结果该复制构造函数/赋值运算符将被调用。但如果你显式声明它们为delete,行为会改变,因为显式删除的函数参与重载决议,它们将被优先选择,然后std::is_move_constructiblestd::is_move_assignable 将返回 false

关于c++ - 不可能的隐式 move 操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41939555/

相关文章:

c++ - istream_iterator 遍历二进制文件中的字节

c++ - 从 'const char**' 到 'char* const*' 的无效转换

c++ - 通过指向非多态类型的基类的指针获取已分配内存的地址

class - 关联类唯一性

c++ - 如果从未通过它们进行修改,是否允许对实际 const 对象的引用进行 const 转换?

c++ - 保持动态分配数组而不用担心删除的最佳方法?

c++ - 编译cpp源仅在c支持下运行

c++ - 删除 vector 中的值时没有匹配的成员函数调用 'erase'

c++ - 像访问一维数组一样访问二维数组是否合法?

c++ - C++ Stringstream初始化