I have a problem with misleading error messages, when I try to compile the following minimal sample in 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(50): note: compiler has generated 'Rect::operator =' here
The compiler tries to tell me that, the copy constructor of class Rect is a deleted function. 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. 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. 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();
.
You then say:
The compiler tries to tell me that, the copy constructor of class Rect is a deleted function
However, you misread the error. The error is about the function operator =
, which is called copy assignment operator. This is a different function to copy constructor, which would look like 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); writing = 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.
In Standard C++ it is not permitted to have an anonymous struct, let alone an anonymous struct inside an anonymous union. 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.
A version of your Rect
that is compilable in Standard C might look like:
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. 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:
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.
Vector
has a non-trivial operator=
, because you write your own body for it. 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.
Note that there is no error about the copy-constructor: Vector
does have a trivial copy-constructor, so the union does too.
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;
operator=
for the Rect
classNow, how would you write your own operator=
? Do you do s1 = other.s1;
, or do you do s2 = other.s2;
? The compiler can't know that on its own, which is the reason behind the implicitly-declared operator=
being deleted.
Now, it seems you overlooked (either accidentally or deliberately) the rule about active members in 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;
. Or if s2
is the last member set, you'd have to do 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.
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.
Reiterating: In standard C++ it is not permitted to read a member of a union other than the one most recently written to. 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.
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.
The error is from the union placing the memory usage of the floats p1x, ... p2y
on top of the allocation for the Vector objects.
g++ would give a more explicit error message informing that an object with a constructor cannot be used in a union.
I'm surprised VS does not report an error on the use of an object in a union directly. It would be interesting to see what happens if you declare the Vectors AFTER the floats in your union.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With