I was under the impression that array were non copyable (or assignable).
int x[5] = {1,2,3,4,5};
int y[5] = {6,7,8,9,0};
x = y; // Fails to compile
But when I put an array inside a class the copy constructor and assignment operator work (I would say as expected but its not what I expected).
#include <iostream>
struct X
{
virtual ~X(){} // Just in case it was something to do with POD
// make sure its not a POD
int x[5];
};
int main()
{
X a;
a.x[0] = 0;
a.x[1] = 1;
a.x[2] = 2;
a.x[3] = 3;
a.x[4] = 4;
// Make a copy of a and test it
X b(a);
std::cout << a.x[0] << " : " << b.x[0] << "\n";
b.x[0] = 10;
b.x[1] = 11;
b.x[2] = 12;
b.x[3] = 13;
b.x[4] = 14;
// Now that we have modified 'b' make sure it is unique.
std::cout << a.x[0] << " : " << b.x[0] << "\n";
// Use assignment and see if it worked.
b = a;
std::cout << a.x[0] << " : " << b.x[0] << "\n";
}
Compile and Run
> g++ t.cpp
> ./a.out
0 : 0
0 : 10
0 : 0
What is going on here?
The defaulted copy constructor and assignment operator use copy construction and assignment individually on each member. When there's an array, copy construction or assignment is used on each element of the array (which is quite well-defined).
Here's the rule, from section 12.8 ([class.copy]
):
The implicitly-defined copy/move constructor for a non-union class
X
performs a memberwise copy/move of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See also the example in 12.6.2. — end note ] The order of initialization is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Letx
be either the parameter of the constructor or, for the move constructor, an xvalue referring to the parameter. Each base or non-static data member is copied/moved in the manner appropriate to its type:
- if the member is an array, each element is direct-initialized with the corresponding subobject of
x
;- if a member m has rvalue reference type
T&&
, it is direct-initialized withstatic_cast<T&&>(x.m)
;- otherwise, the base or member is direct-initialized with the corresponding base or member of
x
.
and
The implicitly-defined copy/move assignment operator for a non-union class
X
performs memberwise copy/move assignment of its subobjects. The direct base classes ofX
are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members ofX
are assigned, in the order in which they were declared in the class definition. Letx
be either the parameter of the function or, for the move operator, an xvalue referring to the parameter. Each subobject is assigned in the manner appropriate to its type:
- if the subobject is of class type, as if by a call to operator= with the subobject as the object expression and the corresponding subobject of x as a single function argument (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
- if the subobject is an array, each element is assigned, in the manner appropriate to the element type;
- if the subobject is of scalar type, the built-in assignment operator is used.
The rule for signature selection between C::C(const C&)
vs C::C(C&)
et al also includes language referring to the array element type.
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