在LLVM中调用隐式删除的复制构造函数

时间:2013-10-24 07:23:16

标签: c++ c++11 default-copy-constructor

根据C ++ 11规则,默认情况下会生成6件事(默认构造函数,复制构造函数,移动构造函数,复制赋值,移动赋值和析构函数)。根据第二个规则,当定义任何自定义副本,移动或析构函数时,不会生成这些默认操作。 但是在我之后的代码中并非如此。但是这段代码无法编译并出现错误

call to implicitly deleted copy constructor of 'Uni'

当我为Uni编写自己的复制构造函数时,一切正常。 (在代码中注释,供参考)

任何想法都非常感激。

最后,我在Mac上运行它,使用LLVM编译器运行Xcode。

非常感谢...

#include <iostream>

class A
{
public:
    A(int i) :num{i}
    {
        std::clog<< "ctor  A() num = " << num << "\n";

    }
    A( A const &aRef)
    :num{aRef.num}
    {
        std::clog << " copy ctor A( A const &aRef) num = " << num << "\n";
    }

    int value()
    {
        return num;
    }

private:
    int num;

};
class Uni
{

public:
    Uni(A* aptr) : up{aptr}
    {
        std::clog << " ctor Uni value = " << up.get()->value() << "\n";
    }
    /*Uni(Uni const &uRef)
    {
        std::clog << " copy ctor Uni copying obj pointed by unique_ptr\n";
        up.reset(uRef.up.get() ? new A{*uRef.up.get()} : nullptr);
    }*/
private:
    std::unique_ptr<A> up;

};

int main(int argc, const char * argv[])
{
    Uni one{new A{10}};
    Uni two{one}; //default copy ctor is implicitly deleted. why ?
}

1 个答案:

答案 0 :(得分:38)

自动生成特殊成员的C ++ 11规则并不像发布它们那么简单。最重要的区别在于,在某些情况下,成员是隐式声明的,但定义为已删除。这就是你的情况。

C ++ 11,[class.copy]§11:

  

如果XX,则X类的默认复制/移动构造函数被定义为已删除(8.4.3):

     
      
  • 具有非平凡对应构造函数的变体成员,M是类似联合的类,
  •   
  • 类型M(或其数组)的非静态数据成员,由于重载解析(13.3)而无法复制/移动,因为应用于B的相应构造函数,导致模糊或从默认构造函数中删除或无法访问的函数,
  •   
  • 无法复制/移动的直接或虚拟基类B,因为重载决策(13.3),应用于{{1}}的相应构造函数,会导致歧义或被删除的函数或者从违约的构造函数中无法访问,
  •   
  • 具有从默认构造函数中删除或无法访问的析构函数的类型的任何直接或虚拟基类或非静态数据成员,
  •   
  • 表示复制构造函数,rvalue引用类型的非静态数据成员,或
  •   
  • 用于移动构造函数,非静态数据成员或直接或虚拟基类,其类型不具有移动构造函数,并且不易于复制。
  •   

(强调我的)


更一般地说,自动生成的类成员的规则是:

  • 如果该类没有用户提供的构造函数,则声明一个默认构造函数。

  • 如果该类没有用户提供的复制构造函数,则声明一个。

  • 如果该类没有{用户提供的复制或移动构造函数,用户提供的复制或移动赋值运算符,用户提供的析构函数},则将声明移动构造函数(但请参阅下面的(*))

  • 如果该类没有用户提供的复制赋值运算符,则声明一个。

  • 如果该类没有{用户提供的复制或移动构造函数,用户提供的复制或移动赋值运算符,用户提供的析构函数},则将声明移动赋值运算符(但请参见下面的(*) )。

  • 如果该类没有用户提供的析构函数,则声明一个。

任何自动声明的成员都可以定义为默认(执行默认的东西)或定义为已删除(如果您尝试使用它,则会出现错误)。经验法则是“如果默认版本有意义,它将被定义为默认值。否则,它将被定义为已删除。”

在这种情况下,“有意义”意味着“不会试图调用已删除,含糊不清,无法访问或其他非法的功能”。例如,我在本答案第一部分引用的标准位列出了对复制构造函数没有“意义”的内容。

此外,如果类具有用户提供的移动构造函数或移动赋值运算符,则自动声明的复制构造函数或复制赋值运算符被定义为已删除。

(*)如果自动声明的移动构造函数或移动赋值运算符将被定义为已删除,则根本不会声明它。该规则存在,因此尝试隐式移动这样的类会回退到复制它而不是生成错误。