C2280:尝试引用已删除的函数(联合、结构、复制构造函数)
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::运算符 =(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
这是相当清楚的:带有参数 const Rect &
的成员函数operator=
已被delete
d,但您的代码尝试在行test = Rect();
上调用它。
然后你说:
编译器试图告诉我,类 Rect 的复制构造函数是一个已删除的函数
但是,您误读了错误。 该错误与函数 operator =
有关,称为复制赋值运算符。 这是一个与复制构造函数不同的函数,看起来像Rect::Rect(const Rect &)
。
您
说您尝试添加:
Rect& operator=(const Rect&) = default;
然而,这不会有什么区别。编译器生成的operator=
函数delete
d,因为编译器无法生成一个(对此的解释如下(;写= default;
不会改变这一点。 您必须实际编写自己的正文,以便执行任务发生时要发生的操作operator=
。
在标准C++中,不允许有匿名结构,更不用说匿名联合中的匿名结构了。所以你在这里真的是独自一人。您的编译器使用的有关operator=
、复制构造函数等的规则不受任何标准保护。
可在标准 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
联合。
Vector
有一个不平凡的operator=
,因为你为它写了自己的身体。因此S1
具有非平凡operator=
,因为它有一个具有非平凡operator=
的成员,因此根据上面的引用,隐含声明的联合operator=
是delete
d。
请注意,复制构造函数没有错误:Vector
确实有一个普通的复制构造函数,所以联合也是如此。
要修复此错误,您可以执行以下两项操作之一:
- 将
Vector::operator=
更改为微不足道,要么完全删除您的定义,要么使其= default;
- 为
Rect
类编写operator=
现在,您将如何编写自己的operator=
? 你做s1 = other.s1;
,还是做s2 = other.s2;
?编译器自己无法知道这一点,这就是隐式声明operator=
被删除的原因。
现在,您似乎忽略了(有意或故意(有关C++中活跃成员的规则:
在联合中,最多一个非静态数据成员可以随时处于活动状态
这意味着如果s1
是最后一个成员集,那么您必须执行s1 = other.s1;
。 或者,如果s2
是最后一个成员集,则必须执行s2 = other.s2;
。
复制构造函数不会遇到此问题,因为它是微不足道的:编译器可以生成按位副本,并且无论哪个成员处于活动状态,它都将正确实现副本。但是,由于您的operator=
并非微不足道,因此这是不可能的。
例如,假设您实际上具有 std::string
和 std::vector
的并集 - 按位复制不适用于其中任何一个,您需要知道哪一个处于活动状态才能执行复制。
重申:在标准C++中,除了最近写信给的工会成员外,不允许阅读工会成员。不能将联合用于别名。 C++还有其他语言工具可以实现您在 C 中使用联合别名可能执行的操作,请参阅此处以获取更多讨论。
根据匿名结构的成员选择,我怀疑这就是您的意图。 如果你真的想继续这种方法,依靠你的编译器实现联合别名作为非标准扩展,那么我的建议是使用默认的operator=
来表示你的Vector
类。
错误来自将浮点数p1x, ... p2y
的内存使用放在 Vector 对象的分配之上的联合。
g++ 会给出更明确的错误消息,通知具有构造函数的对象不能在联合中使用。
我很惊讶 VS 没有直接报告在联合中使用对象的错误。看看如果您在联合中的浮点数之后声明向量会发生什么会很有趣。
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 如何将结构/联合数组的成员传递到函数中
- error dllimport 函数的定义不允许在一个特定的联合中,而其他类、结构和联合将按预期导出
- 为什么继承的结构成员在联合中无法访问?
- 在向量 C++14(无限制联合)的结构内的联合中创建和存储字符串
- 尝试初始化结构内的联合时出错
- 嵌套在联合中的结构嵌套在 C 中的结构中
- 成员引用基类型 'char' 不是 C++ 中的结构或联合
- 结构内部结构的联合 - 混合 C 和 C++
- 联合初始化中的结构
- 成员引用基类型不是结构或联合
- 在结构中使用联合的目的是什么?
- 错误 C2228 左侧'.setDay'必须具有类/结构/联合
- 如何从平面缓冲区中反序列化联合结构的 void* 值的大小
- 联合结构 C# - 等效或等于
- 将C++联合结构转换为 VB6
- C ++:联合 -> 结构,解释?
- 编译器特定联合结构实现的标准替代方案
- 具有构造函数的类的匿名联合/结构
- 联合结构有什么作用