【问题标题】:Call to implicitly deleted copy constructor in LLVM调用 LLVM 中隐式删除的复制构造函数
【发布时间】:2013-11-02 18:44:33
【问题描述】:

根据 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 ?
}

【问题讨论】:

  • unique_ptr不能被复制,导致Uni的复制构造函数被定义为删除,导致编译错误。当您自己编写时,您显然不会遇到这个问题。

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


【解决方案1】:

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

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

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

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

(强调我的)


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

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

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

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

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

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

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

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

在这种情况下,“有意义”是指“不尝试调用已删除、模棱两可、不可访问或其他非法的函数”。例如,我在这个答案的第一部分引用的标准位列出了对复制构造函数没有“意义”的内容。

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

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

【讨论】:

  • 感谢您的详尽回答.. 真的很有帮助
  • @cpp_hex 如果某个答案解决了您的问题,您应该将其标记为已接受(通过单击旁边的绿色勾号;每个问题最多接受一个答案)。 SO works就是这样。
  • “此外,如果类具有用户提供的移动构造函数或移动赋值运算符,则将自动声明的复制构造函数或复制赋值运算符定义为已删除。” -> 这个角色在 VS2013 中不成立。
  • 这是一个很好的答案,但它没有显示如何定位非法使用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
相关资源
最近更新 更多