编译器错误 C2280

“declaration”:尝试引用已删除的函数

编译器检测到尝试引用 deleted 函数。 调用源代码中已显式标记为 = deleted 的成员函数可能会导致此错误。 调用编译器自动声明并标记为 deleted 的结构或类的隐式特殊成员函数也可能导致此错误。 有关编译器何时自动生成 defaultdeleted 特殊成员函数的详细信息,请参阅特殊成员函数

示例:显式删除的函数

调用显式 deleted 函数会导致此错误。 显式 deleted 成员函数意味着该类或结构被有意设计为防止其使用,因此要解决此问题,应更改代码以避免这种情况。

// C2280_explicit.cpp
// compile with: cl /c /W4 C2280_explicit.cpp
struct A {
    A();
    A(int) = delete;
};

struct B {
    A a1;
    A a2 = A(3); // C2280, calls deleted A::A(int)
    // To fix, remove the call to A(int)
};

void f() {
    B b;    // calls implicit B::B(void)
}

示例:未初始化的数据成员

未初始化的引用类型数据成员或 const 数据成员会导致编译器隐式声明 deleted 默认构造函数。 若要解决此问题,请在声明数据成员时初始化该成员。

// C2280_uninit.cpp
// compile with: cl /c C2280_uninit.cpp
struct A {
    const int i; // uninitialized const-qualified data
    // members or reference type data members cause
    // the implicit default constructor to be deleted.
    // To fix, initialize the value in the declaration:
    // const int i = 42;
} a;    // C2280

示例:引用和 const 数据成员

const 或引用类型数据成员会导致编译器声明 deleted 复制赋值运算符。 一旦初始化,这些成员就不能被分配到,所以简单的复制或移动是行不通的。 要解决此问题,建议更改逻辑以删除导致错误的分配操作。

// C2280_ref.cpp
// compile with: cl /c C2280_ref.cpp
extern int k;
struct A {
    A();
    int& ri = k; // a const or reference data member causes
    // implicit copy assignment operator to be deleted.
};

void f() {
    A a1, a2;
    // To fix, consider removing this assignment.
    a2 = a1;    // C2280
}

示例:可移动删除隐式副本

如果类声明了移动构造函数或移动赋值运算符,但没有显式声明复制构造函数,则编译器会隐式声明复制构造函数并将其定义为 deleted。 类似地,如果类声明了移动构造函数或移动赋值运算符,但没有显式声明复制赋值运算符,则编译器会隐式声明复制赋值运算符并将其定义为 deleted。 若要解决此问题,必须显式声明这些成员。

如果收到与 unique_ptr 有关的错误 C2280,则几乎可以肯定是因为你尝试调用其复制构造函数(此函数是一个 deleted 函数)。 根据设计,不能复制 unique_ptr。 使用移动构造函数来转移所有权。

// C2280_move.cpp
// compile with: cl /c C2280_move.cpp
class base
{
public:
    base();
    ~base();
    base(base&&);
    // Move constructor causes copy constructor to be
    // implicitly declared as deleted. To fix this
    // issue, you can explicitly declare a copy constructor:
    // base(base&);
    // If you want the compiler default version, do this:
    // base(base&) = default;
};

void copy(base *p)
{
    base b{*p};  // C2280
}

示例:变体和易失成员

Visual Studio 2015 Update 2 之前的编译器版本不一致,为匿名联合生成默认构造函数和析构函数。 它们现在隐式声明为 deleted。 这些版本还允许在具有 volatile 成员变量的类和结构中对 default 复制和移动构造函数以及 default 复制和移动赋值运算符进行非符合性隐式定义。 编译器现在认为这些版本具有重要的构造函数和赋值运算符,并且不会生成 default 实现。 当此类为某一联合(或类中的匿名联合)的成员时,会将联合或类的复制和移动构造函数以及复制和移动赋值运算符隐式定义为 deleted。 若要解决此问题,必须显式声明所需的特殊成员函数。

// C2280_variant.cpp
// compile with: cl /c C2280_variant.cpp
struct A {
    A() = default;
    A(const A&);
};

struct B {
    union {
        A a;
        int i;
    };
    // To fix this issue, declare the required
    // special member functions:
    // B();
    // B(const B& b);
};

int main() {
    B b1;
    B b2(b1);  // C2280
}

示例:已删除间接基成员

Visual Studio 2015 Update 2 之前的编译器版本不一致,允许派生类调用间接派生的 private virtual 基类的特殊成员函数。 进行此类调用时,编译器现在会发出编译器错误 C2280。

在此示例中,类 top 间接派生自专用虚拟 base。 在符合性代码中,这会使 top 无法访问 base 的成员;不能默认构造或销毁 top 类型的对象。 若要在依赖旧编译器行为的代码中解决此问题,请将中间类更改为使用 protected virtual 派生,或将 top 类更改为使用直接派生:

// C2280_indirect.cpp
// compile with: cl /c C2280_indirect.cpp
class base
{
protected:
    base();
    ~base();
};

class middle : private virtual base {};
// Possible fix: Replace line above with:
// class middle : protected virtual base {};
class top : public virtual middle {};    // C4594, C4624
// Another possible fix: use direct derivation:
// class top : public virtual middle, private virtual base {};

void destroy(top *p)
{
    delete p;  // C2280
}