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

52

根据C++11规则,将默认生成6个函数(默认构造函数、拷贝构造函数、移动构造函数、赋值构造函数、移动赋值函数和析构函数)。根据第二条规则,当定义任何自定义的拷贝、移动或析构函数时,那些默认操作将不会被生成。

但是在我的代码中却不是这样的。但是此代码无法编译并显示错误。

call to implicitly deleted copy constructor of 'Uni'

当我为 Uni 编写自己的复制构造函数时,一切都运行良好。(代码有注释,仅供参考)

非常感谢任何想法。

最后,我在 Mac 上使用 Xcode 和 LLVM 编译器运行此程序。

非常感谢...

#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 ?
}

9
unique_ptr 无法复制,这导致 Uni 的复制构造函数被定义为删除,从而导致编译错误。如果你自己编写代码,则不会出现这个问题。 - Simple
1个回答

58

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

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

 

如果类X具有以下特征,则类X的默认复制/移动构造函数将定义为已删除(8.4.3):

 
     
  • 一个具有非平凡对应构造函数的变体成员,且X是类似联合体的类
  •  
  • 一个类类型M(或其数组)的非静态数据成员不能被复制/移动,因为作用于M的相应构造函数的重载决议(13.3)导致二义性或已删除或从默认构造函数中无法访问的函数
  •  
  • 一个直接或虚基类B不能被复制/移动,因为作用于B的相应构造函数的重载决议(13.3)导致二义性或已删除或从默认构造函数中无法访问的函数
  •  
  • 任何具有已删除或从默认构造函数中无法访问的析构函数的直接或虚基类或非静态数据成员
  •  
  • 对于复制构造函数,一个具有右值引用类型的非静态数据成员,或
  •  
  • 对于移动构造函数,一个具有不具有移动构造函数且不是平凡可复制的类型的非静态数据成员或直接或虚基类。
  •  

(强调我的)


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

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

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

  • 如果类没有以下任何一个:用户提供的拷贝或移动构造函数、用户提供的拷贝或移动赋值运算符、用户提供的析构函数,则会声明一个移动构造函数(但见 (*))。

  • 如果类没有用户提供的拷贝赋值运算符,则会声明一个。

  • 如果类没有以下任何一个:用户提供的拷贝或移动构造函数、用户提供的拷贝或移动赋值运算符、用户提供的析构函数,则会声明一个移动赋值运算符(但见 (*))。

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

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

    在这个上下文中,“有意义”意味着“不会尝试调用已删除、模糊、无法访问或其他非法的函数”。例如,我在答案的第一部分引用的标准位列出了哪些内容对于拷贝构造函数来说并不“有意义”。

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

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


感谢您详细的回答,真的很有帮助。 - cpp_hex
如果一个答案解决了你的问题,你应该将其标记为已接受的答案(通过点击旁边的绿色勾号;每个问题最多只能有一个已接受的答案)。这就是 SO 的工作方式 - Angew is no longer proud of SO
此外,如果类具有用户提供的移动构造函数或移动赋值运算符,则自动声明的复制构造函数或复制赋值运算符将被定义为已删除。但这个规则在VS2013中不适用。 - TonySalimi
1
这是一个很好的答案,但它没有展示如何定位非法使用! - Konchog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接