【问题标题】:Enforcing explicitly defaulted special member function generation强制执行显式默认的特殊成员函数生成
【发布时间】:2016-02-04 15:17:50
【问题描述】:

在 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中的move构造函数,因为这样做会导致编译错误(A的move构造函数被删除)。如果不显式删除 A 的构造函数,B 的移动构造函数将按预期生成(复制 A,而不是移动它)。

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

B b;
B b2 (std::move(b)); // Will call B's copy constructor

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

【问题讨论】:

  • 你不知道你加入的成员是否可以移动吗?
  • 它们在最初实现类时可能是可移动的,但是,如果以后添加更多成员,则可能会忽略可移动要求(尤其是如果它们是由其他人添加的)。
  • 应该注意,删除其移动构造函数但其复制构造函数的类型非常...奇怪。这样做绝对没有任何收获。所以最好忽略这种情况,认为它是某人无缘无故做一些愚蠢的事情的结果。
  • @JanHolecek:有关这方面的更多详细信息,请参阅this question
  • 定义类外的函数为默认值。

标签: c++ xcode c++11 clang


【解决方案1】:

有一种方法可以检测像A 这样的类型。但仅当类型 显式 删除移动构造函数时。如果移动构造函数被隐式生成为已删除,则它不会参与重载决议。这就是为什么B 是可移动的,即使A 不是。 Bdefaults 移动构造函数,这意味着它被隐式删除,所以会发生复制。

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 的意思是“做你通常会做的事情”,它还包括这种特殊的重载决议行为,它忽略了隐式删除的移动构造函数。

【讨论】:

    猜你喜欢
    • 2013-09-14
    • 2015-03-04
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 2015-10-07
    相关资源
    最近更新 更多