繁体   English   中英

C2280:尝试引用已删除的函数(union,struct,copy constructor)

[英]C2280: attempting to reference a deleted function (union, struct, copy constructor)

当我尝试在Visual Studio 2015中编译以下最小示例时,我遇到了误导性错误消息的问题:

class Vector
{
    float x;
    float y;

public:

    Vector(float x, float y) : x(x), y(y) {}
    Vector& operator = (const Vector& v) { x = v.x; y = v.y; return *this; }
    //Vector(Vector&&) = default;
};


class Rect
{
public:
    union {
        struct {
            Vector p1, p2;
        };

        struct {
            float p1x, p1y, p2x, p2y;
        };
    };

    Rect() : p1(0,0),  p2(0,0) {}
    Rect(Vector& p1,  Vector& p2) : p1(p1), p2(p2) {}

    /*Rect(const Rect&) = default;
    Rect& operator=(const Rect&) = default;
    Rect& operator=(Rect&&) = default;
    Rect(Rect&&) = default;*/
};


int main()
{
    Rect test = Rect();
    test = Rect();
    return 0;
}

我收到以下错误消息:

1> ... main.cpp(56):错误C2280:'Rect&Rect :: operator =(const Rect&)':尝试引用已删除的函数

1> ... main.cpp(50):注意:编译器在这里生成了'Rect :: operator ='

编译器试图告诉我,类Rect的复制构造函数是一个已删除的函数。 所以我尝试添加各种附加(复制)构造函数和赋值运算符,如下所示,但没有成功:

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

我认识到错误实际上不是在Rect类中引起的。 当我评论这条线

Vector& operator = (const Vector& v) { x = v.x; y = v.y; return *this; }

错误令人失望,当我想保留这一行时,我必须添加以下行:

Vector(Vector&&) = default;

但是,只有当我在Rect类中使用联合和结构时,这个问题才会出现。 所以我不知道,我的错误实际上是在哪里造成的,或者只是错误消息指向了错误的类。

重复错误消息:

main.cpp(56): error C2280: 'Rect &Rect::operator =(const Rect &)': attempting to reference a deleted function

这很清楚:成员函数operator= with参数const Rect &已被delete d,但是你的代码试图在行test = Rect();

然后你说:

编译器试图告诉我,类Rect的复制构造函数是一个已删除的函数

但是,您误读了错误。 错误是关于函数operator = ,它被称为复制赋值运算符 这是复制构造函数的不同函数,它看起来像Rect::Rect(const Rect &)


你说你试过添加:

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

然而,这没有任何区别。 编译器生成的operator=函数是delete d,因为编译器不可能生成一个(下面对此进行解释); 写作= default; 不会改变这一点。 您必须为operator=实际编写自己的主体,它执行您在分配发生时要执行的操作。


在标准C ++中,不允许使用匿名结构,更不用说匿名结合中的匿名结构。 所以你真的在这里独自一人。 编译器使用的有关operator= ,copy构造函数等的规则不受任何标准的约束。

可在标准C中编译的Rect版本可能如下所示:

class Rect
{
public:
    struct S1 {
        Vector p1, p2;
        S1(Vector p1, Vector p2): p1(p1), p2(p2) {} 
    };
    struct S2 {
        float p1x, p1y, p2x, p2y;
    };

    union {
        struct S1 s1;
        struct S2 s2;
    };

    Rect() : s1({0, 0}, {0, 0}) {}
    Rect(Vector p1,  Vector p2) : s1(p1, p2) {}
};

到现在为止还挺好。 对于此类,隐式声明的operator=定义为已删除 为了了解原因,我们首先要查看匿名联合的隐式声明的特殊函数,因为类的隐式声明函数的行为取决于每个成员的相同操作的行为。

联合的相关规则是C ++ 14 [class.union] / 1:

如果联合的任何非静态数据成员具有非平凡的默认构造函数,复制构造函数,移动构造函数,复制赋值运算符,移动赋值运算符或析构函数,则联合的相应成员函数必须是用户提供的,否则它将为联盟隐式delete d。

Vector有一个非平凡的operator= ,因为你为它编写了自己的主体。 因此S1具有非平凡的operator= ,因为它具有非平凡的operator=的成员,因此根据上面的引用,对于union的隐式声明的operator=delete d。

请注意,复制构造函数没有错误: Vector 确实有一个简单的复制构造函数,因此union也是如此。


要修复此错误,您可以执行以下两项操作之一:

  • 通过完全删除你的定义,或者使它= default; ,将Vector::operator=改变为微不足道的= default;
  • operator=Rect

现在,您将如何编写自己的operator= 你做s1 = other.s1; ,或者你做s2 = other.s2; 编译器无法自己知道,这是隐式声明的operator=被删除的原因。

现在,您似乎忽略了(无论是偶然还是故意)关于C ++中活动成员的规则:

在联合中,至多一个非静态数据成员可以随时处于活动状态

这意味着如果s1是最后一个成员集,那么你必须做s1 = other.s1; 或者,如果s2是最后一个成员集,则必须执行s2 = other.s2;

复制构造函数不会遇到这个问题,因为它很简单 :编译器可以生成按位复制,并且无论哪个成员处于活动状态,都能正确实现复制。 但由于你的operator=非常重要,这是不可能的。

例如,假设您实际上有一个std::stringstd::vector - 按位复制不适用于其中任何一个,并且您需要知道哪个是活动的才能执行复制。


重申: 在标准C ++中,不允许读取最近写入的联合的成员 您不能使用联合进行别名。 C ++有其他语言工具可以实现你在C中使用联合别名做的事情, 请参阅此处以获得更多讨论

根据您对匿名结构的成员选择,我怀疑这是您打算做的。 如果你真的想继续这种方法,依靠你的编译器实现联合别名作为非标准的扩展,那么我的建议是使用默认的operator=为你的Vector类。

错误来自联合将浮点数p1x, ... p2y的内存使用放在Vector对象的分配之上。

g ++会给出一个更明确的错误消息,告知带有构造函数的对象不能在union中使用。

我很惊讶VS没有直接报告在联合中使用对象的错误。 如果你在你的联盟中声明了浮动之后会发生什么,那将会很有趣。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM