2

After reading up on list-initialization and its various flavours, I decided to test out some features in an openGL ES app I'm writing using Xcode and Objective-C++, until I ran into something rather obscure.

I've known about (and frequently implement) the conventional C style structure initialization using the following syntax to initialize POD, such as a GLKVector2, for example:

GLKVector2 point = { 0,0 };    //copy-initialized

but this approach may not always be what's intended by the programmer. So, by removing the assignment (and a needless copy construction operation), in favour of direct-initilization, one would assume (from the documentation) the above declaration would appear like so:

GLKVector2 point{ 0,0 };    //direct-initialized, but generates compile error

However, the compiler doesn't complain when the code looks like this:

GLKVector2 point{ { 0,0 } };

To me, this appears as point is being direct-initialized from a temporary created from the inner structure { 0,0 } and thus not offering any advantage over the first approach; the temporaries still have to be allocated and deallocated.

Or perhaps this issue is simply the nature of the union/struct layout used by GLKit types confusing the compiler.

Some clarification on this odd syntax, before further implementation in the code, would be much appreciated

ildjarn
  • 62,044
  • 9
  • 127
  • 211
vigilance
  • 116
  • 1
  • 11
  • 1
    Could you post the definition of `GLKVector2`? Constructors should be enough – Andy Prowl Feb 27 '13 at 20:55
  • Also, what compiler version are you using? – Nicol Bolas Feb 27 '13 at 21:34
  • 2
    "*and a needless copy construction operation*" FYI: you didn't remove one of these because one of those never happened. Copy-initialization doesn't actually copy anything. And copy-list-initialization certainly does not. – Nicol Bolas Feb 27 '13 at 21:35

2 Answers2

4

The outer braces delimit the initializer for the object itself, the inner braces are the initializer for a member inside the object, e.g.

GLKVector2 v = { initializers-for-members };

where initializers-for-members is { 0, 0 } because the type has an array member, of two elements, and you initialize an array of two members with { a, b }.

C++ supports "brace elision" (8.5.1 [dcl.init.aggr] paragraph 11) which means nested braces can be left out of the initializer under certain circumstances, but in C++11 brace elision is only allowed for copy-initialization. This is why you don't need two sets of braces for the copy-init case, but do need them for direct-init.

Since C++11 was completed the rules have been changed by DR 1270 to allow brace elision in the direct-list-initialization case too, but that's a change made to the post-C++11 draft and not widely supported yet.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
3

According to docs GLKVector2 is:

union _GLKVector2
   {
   struct { float x, y; };
   struct { float s, t; };
      float v[2];
};
typedef union _GLKVector2 GLKVector2;

That's why you need double braces, you're initializing by initializing a union member. Member-wise aggregate initialization occurs in-place. If it were a flat structure, your single-braces assumption would work. This also happens when initializing an std::array (e.g. std::array<int, 2> a{{1, 2}}) because it's a POD aggregate, and no temporary C arrays are involved.

You should look at aggregate initialization.

EDIT

Actually, looking up the docs, there're rules about brace elision (only allowed in copy-initialization context) that may be of interest.

Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120
  • 1
    The underlying problem IIRC is that in C++11 brace elision isn't allowed with direct-initialization syntax. I believe this is going to be fixed. Also, the double brace syntax for initializing std::array, `std::array a{{1, 2}}`, is implementation specific. The standard only specifies that `std::array` can be initialized with the copy-initialization syntax. – bames53 Feb 27 '13 at 22:27