Visual Studio 2013 和 2015 中的 C++ 编译器错误 C2280“尝试引用已删除的函数”

C++ Compiler Error C2280 "attempting to reference a deleted function" in Visual Studio 2013 and 2015

提问人:Alessandro Jacopson 提问时间:7/7/2015 更新时间:12/5/2020 访问量:156205

问:

此代码片段在 Visual Studio 2013(版本 12.0.31101.00 Update 4)中编译时没有错误

class A
{
public:
   A(){}
   A(A &&){}
};

int main(int, char*)
{
   A a;
   new A(a);
   return 0;
}

虽然它在 Visual Studio 2015 RC(版本 14.0.22823.1 D14REL)中编译时出现此错误:

1>------ Build started: Project: foo, Configuration: Debug Win32 ------
1>  foo.cpp
1>c:\dev\foo\foo.cpp(11): error C2280: 'A::A(const A &)': attempting to reference a deleted function
1>  c:\dev\foo\foo.cpp(6): note: compiler has generated 'A::A' here
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

我认为 Visual Studio 2015 附带的编译器生成了 Copy 构造函数并将其标记为,因此我收到错误 C2280(顺便说一句,我在 msdn.microsoft.com 上找不到文档)。=delete

现在,假设我有一个代码库,可以使用 Visual Studio 2013 编译(它之所以有效,是因为它依赖于编译器自动生成的代码),但由于 C2015 的原因无法使用 Visual Studio 2280 编译,我该如何解决这个问题?

我想以这种方式声明类:A

class A
{
public:
   A(){}
   A(A &&){}
   A(const A&)=default;
};

我错过了什么吗?

C 可视化-C++ 复制构造函数 C++14

评论

2赞 Jagannath 7/7/2015
除此之外,还有A& operator=(A&&) ;A& operator=(const A&);
1赞 Sahil Singh 3/18/2019
就我而言,我的类中有一个 std::unique_ptr<>,它阻止编译器生成隐式复制构造函数。

答:

4赞 TartanLlama 7/7/2015 #1

如果为类编写用户定义的移动构造函数,则复制构造函数将被删除。这是因为,如果一个类需要其移动构造函数的特殊行为,那么它可能需要在其复制构造函数中具有一些类似的行为,因此将删除复制构造函数,以防止您无意中使用默认行为。

如果要定义自己的移动构造函数并使用默认的复制构造函数,则需要将其声明为 ,就像您在问题中建议的那样:default

class A
{
public:
   A(){}
   A(A &&){}
   //I know what I'm doing, compiler, use the default version.
   A(const A&)=default;
};

请注意,如果定义自定义移动构造函数,则还应考虑赋值运算符和析构函数。

评论

0赞 Johannes Schaub - litb 7/9/2015
在 C++14 中,复制构造函数是否仍可由编译器选择,或者编译器是否会不选择复制构造函数,而是因为它没有可用的构造函数而失败?我想我读到这将会改变,但忘记了它是否在 C++14 中。
72赞 Barry 7/7/2015 #2

从 [class.copy]/7,强调我的:

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

在第18段中,有一个类似的章节,对副本转让有类似的措辞。所以你的班级真的是:

class A
{
public:
   // explicit
   A(){}
   A(A &&){}

   // implicit
   A(const A&) = delete;
   A& operator=(const A&) = delete;
};

这就是为什么你不能复制构建它。如果提供移动构造函数/赋值,并且仍然希望该类是可复制的,则必须显式提供这些特殊成员函数:

    A(const A&) = default;
    A& operator=(const A&) = default;

您还需要声明移动分配运算符。如果你真的需要这些特殊功能,你可能还需要析构函数。见五法则

41赞 doby 5/30/2016 #3

我遇到了同样的问题,这是由于定义不明确的成员变量:

double const deltaBase = .001;

放入此内容将导致删除复制构造函数。去掉“const”并在构造函数中分配。

评论

2赞 Slate 8/31/2016
这里也一样,除了我有一个未设置的引用并遇到这个问题:Foo&foo;
1赞 user3717478 8/14/2017
我在类中有一个导致此错误的 ostringstream 对象。
2赞 Nabs 9/6/2021
我也遇到了这个错误,但我仍然不明白为什么 const 会导致复制构造函数被删除。
5赞 Neelabh Mam 8/19/2019 #4

即使在“默认”复制 ctor 之后,我也遇到了这个错误。原来,我的一个类成员(rapidjson 的 Document 对象)不允许复制。将其更改为引用,通过默认 ctor 初始值设定项列表中的 *(new rapidjson::D ocument()) 进行初始化。看起来除了默认的复制控制之外,所有单个成员也应该是可复制的。

4赞 piyu2cool 11/1/2019 #5

我遇到了类似的情况,我有一个类的层次结构,基类中的析构函数被声明为虚拟的。在这种情况下,编译器不会自动生成移动和复制构造函数。因此,我们必须默认这些方法,以便编译器生成这些方法的定义。

但是,在我默认复制并移动构造函数后,我遇到了另一个问题。我看到编译器仍然无法生成复制和移动构造函数。原因是在基类中使用了 std::atomic 成员变量。由于原子变量不可复制或不可移动,因此编译器无法为复制构造函数生成定义。这让我很头疼,我不得不使用不同的方法来解决问题。 查看我面临的类似问题的其他精彩答案。

参考资料:默认虚拟析构函数是否阻止编译器生成的移动操作?

具有 std::atomic 成员变量的类的复制构造函数/赋值运算符出错

7赞 QingJia Wang 9/27/2020 #6

我遇到了同样的错误,只是因为我误用了 std::unique_ptr。

请注意,std::unique_ptr 是不可复制的,它只能移动。

这是错误的演示。

class word;
class sentence
{
    public:
        sentence();
        ~sentence();

    public:
        // Wrong demonstration, because I pass the parameter by value/copying
        // I should use 'std::shared_ptr< word >' instead.
        sentence(std::initializer_list< std::unique_ptr< word > > sentence);
};

以下代码取自 MSVC 编译器的 STL 库。我们可以看到类unique_ptr的复制构造函数和复制赋值运算符被显式删除了。

    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;

评论

0赞 Andrey 8/20/2022
你关于unique_ptr的笔记为我节省了几个小时的工作。谢谢!
1赞 Hossein 10/3/2020 #7

我今天遇到了这个问题,我的问题是由于同时具有 and 作为成员变量引起的。我最初认为这是因为我不小心将其中一个命名为我之前包含的头文件的名称。std::stringstreamstd::ostreamsstream<sstreamn>

但是更改名称无济于事,我必须完全删除变量才能再次工作!然后我意识到我是这样错误地声明的:ostream

std::ostream some_stream;

虽然它应该是:

...
std::ostream some_stream(&filebuf);

基本上,我最好改用!ofstream