13

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?

Martin York
  • 257,169
  • 86
  • 333
  • 562

2 Answers2

20

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). Let x 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 with static_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 of X are assigned first, in the order of their declaration in the base-specifier-list, and then the immediate non-static data members of X are assigned, in the order in which they were declared in the class definition. Let x 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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • So can we write our own copy constructor and easily do the same thing? – Martin York Feb 06 '12 at 21:14
  • @Loki: I think it should be possible with C++11 uniform initialization syntax, e.g. `C::C(const C& x) : member_array{x.member_array} {}`. Because an array is an aggregate, the aggregate direct-initialization syntax applies. – Ben Voigt Feb 06 '12 at 21:15
  • @LokiAstari: I did in C++03 (in MSVC9). `X::X(const X& rhs) : x(rhs.x) {}` Works for arrays fine. I think it's an oversight on one side or another. [IDEOne accepts it too](http://ideone.com/zg3o9) – Mooing Duck Feb 06 '12 at 21:24
  • @MooingDuck: Do they accept it with language extensions turned off? Comeau rejects that, saying that an array can only be initialized with empty parentheses. – Ben Voigt Feb 06 '12 at 21:34
  • @BenVoigt: Good call. With language extensions off, MSVC9 says "error C2536: 'X::X::x' : cannot specify explicit initializer for arrays" – Mooing Duck Feb 06 '12 at 22:00
4

Arrays are neither copyable nor assignable. However, structure with array members have generated copy construction and copy assignment. This is a special rule: i.e. they don't need to be copyable or assignable themselves.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380