在 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
有没有办法强制编译器生成函数,或者在不能生成函数时发出编译错误?没有这种保证,如果单个删除的构造函数可以禁用整个对象层次结构的移动,就很难依赖默认的移动构造函数。
最佳答案
有一种方法可以检测像 A
这样的类型。但前提是类型显式删除了移动构造函数。如果移动构造函数隐式生成为已删除,则它将不参与重载决议。这就是为什么 B
是可移动的,尽管 A
不是。 B
默认
是移动构造函数,这意味着它会被隐式删除,因此会发生复制。
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
表示“做你通常会做的事”,它还包括忽略隐式删除的移动构造函数的特殊重载解析行为。
关于c++ - 强制执行显式默认的特殊成员函数生成,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35204767/