执行明确默认的特殊成员函数生成

Jan*_*cek 5 c++ xcode clang c++11

在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;
};
Run Code Online (Sandbox Code Playgroud)

B中的移动构造函数不会由编译器生成,因为这样做会导致编译错误(删除A的移动构造函数).在没有明确删除A的构造函数的情况下,B的移动构造函数将按预期生成(复制A,而不是移动它).

试图移动这样的对象将默默使用复制构造函数:

B b;
B b2 (std::move(b)); // Will call B's copy constructor
Run Code Online (Sandbox Code Playgroud)

有没有办法强制编译器生成函数或发出编译错误,如果它不能?如果没有这种保证,如果单个已删除的构造函数可以禁用整个对象层次结构的移动,则很难依赖默认的移动构造函数.

Nic*_*las 4

有一种方法可以检测诸如 之类的类型A。但前提是类型显式删除移动构造函数。如果移动构造函数隐式生成为已删除,则它将不参与重载决策。这就是为什么B虽然A不可移动但仍可移动的原因。B defaults 移动构造函数,这意味着它会被隐式删除,因此会发生复制。

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

现在,没有通用的方法可以使任何包含仅复制类型的类型执行您想要的操作。也就是说,您必须单独对每种类型进行静态断言;您不能在默认移动构造函数中添加某些语法以使移动尝试B失败。

其原因部分与向后兼容性有关。想想所有声明用户定义复制构造函数的 C++11 之前的代码。根据 C++11 中移动构造函数的生成规则,它们都将删除移动构造函数。这意味着这种形式的任何代码T t = FuncReturningTByValue();都会失败,即使它通过调用复制构造函数在 C++98/03 中工作得很好。因此,如果无法生成移动构造函数,则通过复制移动而不是移动来解决此问题。

但由于= default意味着“做你通常会做的事情”,它还包括这种特殊的重载解析行为,该行为会忽略隐式删除的移动构造函数。