我有以下示例:

#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)后发现,如果存在用户定义的析构函数,则不会定义隐式move构造函数。这似乎是问题所在,因为C1具有析构函数,所以未定义move构造函数。果然,如果我删除析构函数或将C1(C1&&) = default;添加到C1,那么它将起作用。

到目前为止,一切都很好。

问题是错误消息没有提到~C1()或move-constructor。它说它正在尝试调用复制构造函数,该复制构造函数在基类中已删除。因此,我尝试将delete中的noncopyable ed函数更改为default ed,并且(惊奇!)也解决了该错误。

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

最佳答案

您不需要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但是确实有一个隐式声明的副本构造函数


  如果类定义未显式声明一个副本构造函数,则将隐式声明一个非显式的构造函数。如果类定义声明了move构造函数或move赋值运算符,则隐式声明的副本
  构造函数定义为删除;否则,将其定义为默认值(8.4)。如果该类具有用户声明的副本分配运算符或用户声明的析构函数,则不建议使用后者。


C1上的完整构造函数集(显式和隐式)如下:

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


因此,尝试从类型为C1的右值构造C1将匹配隐式声明的副本构造函数为最佳匹配(没有其他可行的方法),但是该构造函数为deleted,因为noncopyable的副本构造函数为deleted,因此整个表达式格式错误。

这就是错误消息提到构造函数的原因。该移动构造格式错误,因为与该移动构造最匹配的是复制构造程序格式不正确。它不能提及move构造函数,因为没有move构造函数,并且析构函数与当前表达式无关。当您将基类更改为可复制时,现在C1也变为可复制-因此没有错误。仍然没有移动构造函数,只是现在有一个可行的移动构造候选对象。

10-01 20:44
查看更多