不可能隐式移动操作?

jot*_*tik 3 c++ language-lawyer compiler-bug move-semantics c++11

据我所知[class.copy.ctor][class.copy.assign],A以下代码中的struct 不应该是可移动构造的,也不应该是move-assignable:

#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, "");
Run Code Online (Sandbox Code Playgroud)

然而,两个静态断言都失败了GCC和Clang,这意味着由于某种原因A,移动可分配和移动可构造.

在我的推理中,这不应该是,因为struct A:

  • 没有显式声明移动构造函数
  • 没有明确声明移动赋值运算符
  • 有一个用户声明的复制构造函数
  • 有一个用户声明的复制赋值运算符.
  • 有一个x类型的字段,X不能用任何其他类型直接初始化A::x,因为所有构造函数X都将参与重载决策,这是明确删除的.

这是编译器错误还是我误解了什么?

son*_*yao 6

它们是预期的行为,因为复制构造函数和复制赋值运算符的存在满足MoveConstructible的要求

类不必实现移动构造函数来满足此类型要求:采用const T&argument的复制构造函数可以绑定rvalue表达式.

MoveAssignable.

该类型不必实现移动赋值运算符以满足此类型要求:通过值或作为const Type&获取其参数的复制赋值运算符将绑定到rvalue参数.

注意,std :: is_move_constructiblestd :: is_move_assignable只是检查可以从rvalue参数构造/赋值的指定类型的对象.即使没有移动构造函数/赋值运算符,复制构造函数/赋值运算符也可以完成工作,因为rvalue参数也可以传递给const的左值引用.

编辑

注意您显示的示例代码移动构造函数/赋值运算符根本没有被声明(因为存在用户声明的复制构造函数和复制赋值运算符),所以它们不会影响重载解析和复制构造函数的结果/ assignment运算符将被调用.但是,如果你明确声明他们作为delete,行为可能会改变,因为显式删除功能参与到重载决议,他们将被优先选择,然后std::is_move_constructiblestd::is_move_assignable返回false.