delete / default关键字是否将相应的方法标记为用户定义?

时间:2016-11-01 10:33:58

标签: c++ c++11 copy-constructor move-semantics move-constructor

我有以下代码:

#include <iostream>

class B{
public:
    //this should all be generated by default but
    //throwing them in here just to be sure
    B() = default;
    B(const B& b) = default;
    B& operator=(const B& b) = default;
    B(B&& b) = default;
    B& operator=(B&& b) = default;
};

class A {
public:
    A(B x) : x_(x) {}
    A(const A& a) = delete;
    A& operator=(const A& a) = delete;
    //move operations should be generated by compiler?
private:
    B x_;
};

int main() {
    A a = A(B());
}

我希望编译它并使用它的移动构造函数创建A,但是这会失败并显示以下消息:

  

错误:使用已删除的函数'A :: A(const A&amp;)'        A = A(B());   注意:在此声明        A(const A&amp; a)=删除;

当然,添加移动操作并使用默认关键字标记它们可以解决问题。我应该假设移动操作不是由编译器生成的,为什么会这样? delete关键字是否将方法标记为用户实现,因此不会生成移动操作?为什么复制构造函数更喜欢?我正在使用gcc编译。

2 个答案:

答案 0 :(得分:4)

如果您提供复制构造函数/ operator=的实现,则默认情况下不再生成的移动操作。如果你想要它们,你需要明确告诉你这样做。

根据cppreference:

  

如果没有为类类型提供用户定义的移动构造函数   (struct,class或union),以下所有内容均为真:

     
      
  • 没有用户声明的副本构造函数;
  •   
  • 没有用户声明的副本赋值运算符;
  •   
  • 没有用户声明的移动赋值运算符;
  •   
  • 没有用户声明的析构函数;
  •   
     

然后编译器会将移动构造函数声明为非显式   具有签名T::T(T&&)的类的内联公共成员。

以下是针对此类情况的有用图表:

enter image description here attribution

答案 1 :(得分:2)

= delete你不只是说代码不存在,而是如果代码试图调用你应该得到编译错误。

更具体地说,delete d成员不是简单地从可用选项集中删除的:它们被搜索,如果它们匹配,则会出现编译错误。换句话说,= delete适用于签名,当匹配意味着使用问题,而不是签名时,您不想匹配。

例如delete d个成员do indeed participate in overload resolution

提供= delete 与提供实现完全相同,区别在于如果编译器最终生成将调用已删除函数的代码,则会出现编译错误。如果您提供delete d复制构造函数,则仍提供用户定义的复制构造函数。

您应该注意的另一个细微之处是移动构造函数/赋值的自动生成取决于例如“用户定义的复制构造函数”的存在以及该定义作为严格意义。一些在逻辑上不明显的东西(至少不适合我)是与复制构造函数的签名匹配的模板不是复制构造函数。换句话说:

struct Foo {
    template<typename T>
    Foo(const T& other) { ... }
};

模板可以匹配Foo(const Foo&)不被视为用户定义的复制构造函数(不要寻找深层原因,这是因为标准这样说)因此你仍然得到隐式生成的移动构造函数。如果默认实现不正确,这将是严重问题的根源......