在C ++ 11中,如果自动阻止了隐式生成,则可以显式默认特殊成员函数。
但是,明确默认一个特殊的成员函数只会撤消由于手动声明一些其他特殊成员函数(复制操作,析构函数等)而导致的隐式删除,它不会强制编译器生成函数和代码即使实际上不能生成函数,也被认为是形成良好的。
考虑以下情况:
struct A
{
A () = default;
A (const A&) = default;
A (A&&) = delete; // Move constructor is deleted here
};
struct B
{
B () = default;
B (const B&) = default;
B (B&&) = default; // Move constructor is defaulted here
A a;
};
B中的移动构造函数不会由编译器生成,因为这样做会导致编译错误(删除A的移动构造函数)。如果没有明确删除A的构造函数,B的移动构造函数将按预期生成(复制A,而不是移动它)。
尝试移动此类对象将默默使用复制构造函数:
B b;
B b2 (std::move(b)); // Will call B's copy constructor
有没有办法强制编译器生成函数或发出编译错误,如果不能?如果没有这种保证,如果单个已删除的构造函数可以禁用整个对象层次结构的移动,则很难依赖默认的移动构造函数。
答案 0 :(得分:3)
有一种方法可以检测A
等类型。但仅当显式类型删除移动构造函数时。如果将移动构造函数隐式生成为已删除,则它将不参与重载解析。这就是B
即使A
不可移动的原因。 B
default
是移动构造函数,这意味着它被隐式删除,因此复制就会发生。
B
可以构建。但是,A
不是。所以这很简单:
struct B
{
static_assert(is_move_constructible<A>::value, "Oops...");
B () = default;
B (const B&) = default;
B (B&&) = default; // Move constructor is defaulted here
A a;
};
现在,没有通用方法可以使任何包含仅复制类型的类型执行您想要的操作。也就是说,您必须单独对每种类型进行静态断言;你不能在默认的移动构造函数中加入一些语法来试图移动B
失败。
其原因部分在于向后兼容性。想想所有声明用户定义的拷贝构造函数的前C ++ 11代码。根据C ++ 11中移动构造函数生成的规则,所有这些都会删除移动构造函数。这意味着T t = FuncReturningTByValue();
形式的任何代码都会失败,即使它通过调用复制构造函数在C ++ 98/03中运行得很好。因此,如果无法生成移动构造函数,那么通过复制这个问题可以解决这个问题,而不是移动。
但是由于= default
意味着“做你通常会做的事情”,它还包括这种特殊的重载解析行为,忽略了隐式删除的移动构造函数。