5

I came across this while testing some stuff for another question on initializing aggregates. I'm using GCC 4.6.

When I initialize an aggregate with a list, all the members are constructed in place, without copying or moving. To wit:

int main()
{
  std::array<std::array<Goo,2>,2>
    a { std::array<Goo,2>{Goo{ 1, 2}, Goo{ 3, 4}} ,
        std::array<Goo,2>{Goo{-1,-2}, Goo{-3,-4}} };
}

Let us confirm by making some noisy constructors:

struct Goo
{
  Goo(int, int) { }
  Goo(Goo &&) { std::cout << "Goo Moved." << std::endl; }
  Goo(const Goo &) { std::cout << "Goo Copied." << std::endl; }
};

Upon running, no messages are printed. However, if I make the move constructor private, the compiler complains with ‘Goo::Goo(Goo&&)’ is private, although the move constructor is patently not needed.

Does anyone know if there's a standard requirement for the move constructor to be accessible for aggregate initialization like this?

Community
  • 1
  • 1
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084

2 Answers2

8

Not calling the copy or move constructor is an optimization specifically allowed by the standard (but not required).

To be consistent across compilers, an implementation must check that the constructor could have been called, if it didn't care to optimize it out.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Thanks! I cannot even get GCC to call the move constructor with `-O0`, though -- isn't it surprising that the choice of constructor depends on the compiler? By the way, why can I not say `array a{{1,2}, {3,4}}`? It's perfectly OK to say `Goo g{1,2};`. – Kerrek SB Jun 08 '11 at 16:43
  • @Kerrek - The construct in place optimization is simple enough that gcc does that even at -O0, that is known. `Goo` has a constructor taking two ints, so it works. `std::array` only has it's default constructor, which doesn't take values in that form. It would have needed a constructor from an initializer_list. I believe there is a "bug report" on that. – Bo Persson Jun 08 '11 at 16:53
  • I think std::array is explicitly designed as an aggregate type, so it cannot have any user-defined constructors. I was simply wondering why, given that it's an aggregate, I cannot specify `{1,2}` in the aggregate initialization and have it call the matching constructor a la 8.5.4 clause 3. – Kerrek SB Jun 08 '11 at 17:02
  • @Kerrek - It's about brace elision (or lack thereof) - please see [this](http://stackoverflow.com/questions/6041459/c-vector-of-arrays/6043086#6043086), for example. `array a{{{1,2},{3,4}}}` *ought* to work but apparently gcc is buggy in this respect. – JohannesD Jun 08 '11 at 19:32
  • @Johannes: I posted a follow-up -- are you saying that the follow-up scenario is as expected but the failure of `array a{{{1,2},{3,4}}}` is a bug in GCC? – Kerrek SB Jun 10 '11 at 00:03
  • Uniform initialization allows a loophole to this rule. – ildjarn Nov 16 '11 at 22:44
0

Please allow me to follow up on Bo's answer with a modified case, again in GCC 4.6:

struct Goo
{
  Goo(int x, unsigned int n) : x(x), s(new char[n]) { }
private:
  Goo(const Goo &);
  Goo(Goo &&);
  int x;
  char * s;
};

struct Foo
{
  int a;
  Goo g;
};

void f()
{
  Foo k { 3, {1,2} }; // works
  //Foo t { 1, Goo{5,6} }; // fails
  //Foo r { 0, Goo(7,8) }; // fails
}

Why is the first form of in-place construction OK even without accessible copy/move constructors (note that Goo is patently not POD or aggregate), but the latter two (functionally identical) forms aren't? Which part of the standard says that the compiler has to check for an accessible constructor in the latter two cases but not in the first case?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 2
    §8.5.4/3 describes why this is permissible. Contrast that to §8.5/16/6/2. (How's that for a late response? ;-]) – ildjarn Nov 16 '11 at 22:41
  • @ildjarn: It's much appreciated, although an excerpt would have been even better, so I don't have to switch tasks! – Kerrek SB Nov 16 '11 at 22:44
  • §8.5 is massive indeed -- too big for a comment. :-[ – ildjarn Nov 16 '11 at 22:52
  • @ildjarn: Nice reference to §8.5/16/6/2! So `Foo k{3,{1,2}}` direct-initializes `k.a(3)` and `k.g(1,2)`, and we don't need the inaccesible copy constructors. – Kerrek SB Nov 16 '11 at 22:54
  • To be fair, credit goes to litb for cluing me in on this too. :-] – ildjarn Nov 16 '11 at 22:58