是什么导致移动构造函数被删除

时间:2017-02-01 22:54:38

标签: c++ c++11 constructor

我有以下样本:

#include <vector>

class noncopyable {
protected:
    noncopyable() {}
    ~noncopyable() {}
    noncopyable(const noncopyable&) = delete;
    noncopyable& operator=(const noncopyable&) = delete;
    noncopyable(noncopyable&&) = default;
    noncopyable& operator=(noncopyable&&) = default;
};

class C1 : private noncopyable {
public:
  C1() { }
  ~C1() { }
};

int main() {
    std::vector<C1> v;
    v.emplace_back();
    return 0;
}

我认为它应该可行,因为C1应该是可移动的,因为它的基类是,并且它没有数据成员。相反,我得到一个错误(使用clang ++):

error: call to implicitly-deleted copy constructor of 'C1'
    { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
                                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
.
.
.
note: in instantiation of function template specialization 'std::vector<C1, std::allocator<C1> >::emplace_back<>' requested here
        v.emplace_back();
          ^
note: copy constructor of 'C1' is implicitly deleted because base class 'noncopyable' has a deleted copy constructor
class C1 : private noncopyable {
           ^
note: 'noncopyable' has been explicitly marked deleted here
        noncopyable(const noncopyable&) = delete;

做一点研究(http://en.cppreference.com/w/cpp/language/move_constructor)表明如果有一个用户定义的析构函数,那么就不会定义隐式的移动构造函数。这似乎是问题所在,因为C1有一个析构函数,所以move-constructor没有被定义。果然,如果我要么删除析构函数,要么将C1(C1&&) = default;添加到C1,那么它就可以了。

到目前为止一切顺利。

问题是错误消息没有提到~C1()或移动构造函数。它说它试图调用复制构造函数,它在基类中被删除了。所以我尝试将delete中的noncopyable ed函数更改为default ed而不是(惊喜!),这也解决了错误。

所以我的问题是,这最后一件事与错误或它的纠正有什么关系?如果存在析构函数,那么如果基类具有复制构造函数有什么区别呢?

1 个答案:

答案 0 :(得分:3)

您不需要vector,更简单的例子就是:

C1 a;
C1 b(std::move(a)); // error: C1's copy constructor is deleted

来自[class.copy]:

  

如果类X的定义没有明确声明移动构造函数,则非显式构造函数将被隐式声明为默认值,当且仅当   (9.1) - X没有用户声明的拷贝构造函数,
  (9.2) - X没有用户声明的副本赋值运算符,
  (9.3) - X没有用户声明的移动赋值运算符,以及
  (9.4) - X没有用户声明的析构函数。

C1有一个用户声明的析构函数,因此它没有移动构造函数。 C1 确实具有隐式声明的复制构造函数

  

如果类定义没有显式声明复制构造函数,则隐式声明非显式。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的副本   构造函数定义为已删除; 否则,它被定义为默认(8.4)。如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况。

C1上的完整构造函数,显式和隐式,如下所示:

C1();
C1(C1 const& ) = default; // but also delete
~C1();

因此,尝试从类型为C1的rvalue构造C1将匹配该隐式声明的复制构造函数作为最佳匹配(没有其他可行),但该构造函数为deleted因为noncopyable的复制构造函数是deleted,所以整个表达式都是格式错误。

这就是错误消息提到构造函数的原因。那个移动构造是不正确的,因为移动构造的最佳匹配是复制构造器是不正确的。它不能提及移动构造函数,因为没有移动构造函数,并且析构函数与手头的表达式无关。当您将基类更改为可复制时,现在C1也变为可复制 - 因此没有错误。现在仍然没有移动构造函数,现在它已经成为移动构建的可行候选者。