[英]C2280: attempting to reference a deleted function (union, struct, copy constructor)
I have a problem with misleading error messages, when I try to compile the following minimal sample in Visual Studio 2015: 当我尝试在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;
}
I got the following error messages: 我收到以下错误消息:
1>...main.cpp(56): error C2280: 'Rect &Rect::operator =(const Rect &)': attempting to reference a deleted function 1> ... main.cpp(56):错误C2280:'Rect&Rect :: operator =(const Rect&)':尝试引用已删除的函数
1>...main.cpp(50): note: compiler has generated 'Rect::operator =' here 1> ... main.cpp(50):注意:编译器在这里生成了'Rect :: operator ='
The compiler tries to tell me that, the copy constructor of class Rect is a deleted function. 编译器试图告诉我,类Rect的复制构造函数是一个已删除的函数。 So I tried to add all kinds of additional (copy) constructors and assignment operators like shown below but without success: 所以我尝试添加各种附加(复制)构造函数和赋值运算符,如下所示,但没有成功:
Rect(const Rect&) = default;
Rect& operator=(const Rect&) = default;
Rect& operator=(Rect&&) = default;
Rect(Rect&&) = default;
I recognized that the error actually is not caused in the Rect class. 我认识到错误实际上不是在Rect类中引起的。 When I comment the line 当我评论这条线
Vector& operator = (const Vector& v) { x = v.x; y = v.y; return *this; }
the error disappiers and when I want to keep this line, I have to add the following line: 错误令人失望,当我想保留这一行时,我必须添加以下行:
Vector(Vector&&) = default;
However, this problem seems to show up only if I am using unions and structs inside my Rect class. 但是,只有当我在Rect类中使用联合和结构时,这个问题才会出现。 So I do not know, where my error is actually caused or if just the error message points to the wrong class. 所以我不知道,我的错误实际上是在哪里造成的,或者只是错误消息指向了错误的类。
Repeating the error message: 重复错误消息:
main.cpp(56): error C2280: 'Rect &Rect::operator =(const Rect &)': attempting to reference a deleted function
This is fairly clear: the member function operator=
with parameter const Rect &
has been delete
d, but your code tries to call it on the line test = Rect();
这很清楚:成员函数operator=
with参数const Rect &
已被delete
d,但是你的代码试图在行test = Rect();
. 。
You then say: 然后你说:
The compiler tries to tell me that, the copy constructor of class Rect is a deleted function 编译器试图告诉我,类Rect的复制构造函数是一个已删除的函数
However, you misread the error. 但是,您误读了错误。 The error is about the function operator =
, which is called copy assignment operator . 错误是关于函数operator =
,它被称为复制赋值运算符 。 This is a different function to copy constructor , which would look like Rect::Rect(const Rect &)
. 这是复制构造函数的不同函数,它看起来像Rect::Rect(const Rect &)
。
You say that you tried adding: 你说你试过添加:
Rect& operator=(const Rect&) = default;
However this would make no difference. 然而,这没有任何区别。 The compiler-generated operator=
function is delete
d because it is not possible for the compiler to generate one (explanation for this comes below); 编译器生成的operator=
函数是delete
d,因为编译器不可能生成一个(下面对此进行解释); writing = default;
写作= default;
does not change this. 不会改变这一点。 You have to actually write your own body for operator=
which performs the actions that you want to occur when an assignment happens. 您必须为operator=
实际编写自己的主体,它执行您在分配发生时要执行的操作。
In Standard C++ it is not permitted to have an anonymous struct, let alone an anonymous struct inside an anonymous union. 在标准C ++中,不允许使用匿名结构,更不用说匿名结合中的匿名结构。 So you are really out on your own here. 所以你真的在这里独自一人。 The rules your compiler is using regarding operator=
, copy constructor, etc. are not covered by any Standard. 编译器使用的有关operator=
,copy构造函数等的规则不受任何标准的约束。
A version of your Rect
that is compilable in Standard C might look like: 可在标准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) {}
};
So far, so good. 到现在为止还挺好。 For this class, the implicitly-declared operator=
is defined as deleted . 对于此类,隐式声明的operator=
被定义为已删除 。 To see why , we first have to look at the implicitly-declared special functions for the anonymous union, because the behaviour of implicitly-declared function for a class depends on the behaviour of the same operation for each of its members. 为了了解原因,我们首先要查看匿名联合的隐式声明的特殊函数,因为类的隐式声明函数的行为取决于每个成员的相同操作的行为。
The relevant rule here for the union is C++14 [class.union]/1: 联合的相关规则是C ++ 14 [class.union] / 1:
If any non-static data member of a union has a non-trivial default constructor , copy constructor, move constructor, copy assignment operator, move assignment operator, or destructor, the corresponding member function of the union must be user-provided or it will be implicitly
delete
d for the union. 如果联合的任何非静态数据成员具有非平凡的默认构造函数,复制构造函数,移动构造函数,复制赋值运算符,移动赋值运算符或析构函数,则联合的相应成员函数必须是用户提供的,否则它将为联盟隐式delete
d。
Vector
has a non-trivial operator=
, because you write your own body for it. Vector
有一个非平凡的operator=
,因为你为它编写了自己的主体。 Therefore S1
has non-trivial operator=
, because it has a member with non-trivial operator=
, and so according to the above quote, the implicitly-declared operator=
for the union is delete
d. 因此S1
具有非平凡的operator=
,因为它具有非平凡的operator=
的成员,因此根据上面的引用,对于union的隐式声明的operator=
是delete
d。
Note that there is no error about the copy-constructor: Vector
does have a trivial copy-constructor, so the union does too. 请注意,复制构造函数没有错误: Vector
确实有一个简单的复制构造函数,因此union也是如此。
To fix this error you could do one of two things: 要修复此错误,您可以执行以下两项操作之一:
Vector::operator=
to be trivial, either by removing your definition entirely, or making it = default;
通过完全删除你的定义,或者使它= default;
,将Vector::operator=
改变为微不足道的= default;
operator=
for the Rect
class 写operator=
为Rect
类 Now, how would you write your own operator=
? 现在,您将如何编写自己的operator=
? Do you do s1 = other.s1;
你做s1 = other.s1;
, or do you do s2 = other.s2;
,或者你做s2 = other.s2;
? ? The compiler can't know that on its own, which is the reason behind the implicitly-declared operator=
being deleted. 编译器无法自己知道,这是隐式声明的operator=
被删除的原因。
Now, it seems you overlooked (either accidentally or deliberately) the rule about active members in C++: 现在,您似乎忽略了(无论是偶然还是故意)关于C ++中活动成员的规则:
In a union, at most one of the non-static data members can be active at any time 在联合中,至多一个非静态数据成员可以随时处于活动状态
This means that if s1
is the last member set, then you'd have to do s1 = other.s1;
这意味着如果s1
是最后一个成员集,那么你必须做s1 = other.s1;
. 。 Or if s2
is the last member set, you'd have to do s2 = other.s2;
或者,如果s2
是最后一个成员集,则必须执行s2 = other.s2;
. 。
The copy-constructor doesn't run into this problem because it is trivial : the compiler can generate a bit-wise copy and that will correctly implement the copy regardless of which member was active. 复制构造函数不会遇到这个问题,因为它很简单 :编译器可以生成按位复制,并且无论哪个成员处于活动状态,都能正确实现复制。 But since your operator=
is non-trivial, that would not be possible. 但由于你的operator=
非常重要,这是不可能的。
For example, imagine if you actually had a union of std::string
and std::vector
- bitwise copy doesn't work for either of those and you need to know which one is active in order to perform the copy. 例如,假设您实际上有一个std::string
和std::vector
- 按位复制不适用于其中任何一个,并且您需要知道哪个是活动的才能执行复制。
Reiterating: In standard C++ it is not permitted to read a member of a union other than the one most recently written to . 重申: 在标准C ++中,不允许读取最近写入的联合的成员 。 You can't use unions for aliasing. 您不能使用联合进行别名。 C++ has other language tools to achieve what you might do in C with union aliasing, see here for more discussion . C ++有其他语言工具可以实现你在C中使用联合别名做的事情, 请参阅此处以获得更多讨论 。
Based on the choice of members for your anonymous structs I suspect that this is what you intended to do. 根据您对匿名结构的成员选择,我怀疑这是您打算做的。 If you really want to go ahead with this approach, relying on your compiler implementing union aliasing as a non-standard extension, then my advice would be to use the defaulted operator=
for your Vector
class. 如果你真的想继续这种方法,依靠你的编译器实现联合别名作为非标准的扩展,那么我的建议是使用默认的operator=
为你的Vector
类。
The error is from the union placing the memory usage of the floats p1x, ... p2y
on top of the allocation for the Vector objects. 错误来自联合将浮点数p1x, ... p2y
的内存使用放在Vector对象的分配之上。
g++ would give a more explicit error message informing that an object with a constructor cannot be used in a union. g ++会给出一个更明确的错误消息,告知带有构造函数的对象不能在union中使用。
I'm surprised VS does not report an error on the use of an object in a union directly. 我很惊讶VS没有直接报告在联合中使用对象的错误。 It would be interesting to see what happens if you declare the Vectors AFTER the floats in your union. 如果你在你的联盟中声明了浮动之后会发生什么,那将会很有趣。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.