5

I am working on a petty (pretty) printer for PODs, STLs and Composite Types like Arrays. While doing so, I was also fiddling with Initialization Lists and came across the following declaration

std::vector<double[3]> arr{ { 10, 11, 12 }, { 20, 21, 22 } }; 

It seems both VC2013 and G++ 4.8 is not quite happy and issues a consistent error message which in either case is not quite helpful to me

For VC++: error C3074: an array can only be initialized with an initialize-list

For G++ 4.8: error: array must be initialized with a brace-enclosed initialize

So either Initialization lists cannot be used here or my syntax is quite not right?

On a similar front, the following syntax seems to be valid

std::vector<std::array<int, 3>> arr{ { 10, 11, 12 }, { 20, 21, 22 } };

What is the possible problem with my initialization list?

  • Note I understand I should use std::array instead of C type arrays but I am just experimenting.
  • Note If you wan't to play around with this, here is an IDEONE version
  • Note Also, it would be quite beneficial if you can refer me back to the standard.
asked Aug 15, 2014 at 16:42
5
  • Might it be one of those cases where you need an extra pair of curly braces arr{{{ 10, 11,12}, {20, 21,22}}}? Commented Aug 15, 2014 at 16:44
  • @LevLandau: No, this doesn't solves the problem. Commented Aug 15, 2014 at 16:49
  • In my experience with VC2010, it does not like multiple levels of curly braces in initializers. Try removing all but the outermost level of braces. That has worked for me, although I do not recall having used it with a templates class. Commented Aug 15, 2014 at 16:52
  • @Logicrat: Actually I am using VC2013 which is supposed to support Uniform Initialization Lists. I have cross checked with g++ Commented Aug 15, 2014 at 16:53
  • Vectors require the type to be move-assignable and movable. Commented Aug 15, 2014 at 16:55

2 Answers 2

5

Reading the current C++1y draft standard.

From before Table 99:

T is EmplaceConstructible into X from args , for zero or more arguments args , means that the following expression is well-formed: allocator_traits::construct(m, p, args)

Table 100:

X(il); | Equivalent to | X(il.begin(), il.end());
--------------------+---------------------+--------------------------------
X(i, j); | | Requires:
X a(i, j); | | T shall be EmplaceConstructible
 | into X from *i.

So std::vector<double[3]> v{ {1,2,3}, {4,5,6} }; is valid iff double[3] is EmplaceConstructible from {1,2,3} as an element of an initializer list being passed to a std::vector<double[3]>.

There is a clause about forward iterators as well, but that is no problem (as std::initialzier_list iterators are forward iterators).

std::vector<T> takes an std::initializer_list<T> parameter.

So std::initializer_list<double[3]> is the candidate list.

First, std::initializer_list<double[3]> x = {{1.0, 2.0, 3.0}}; fails to compile in gcc. But suppose that is a bug in gcc.

Second, ::new (nullptr) double[3](std::initializer_list<double>{1.0, 2.0, 3.0}); placement new, which EmplaceConstructable reduces to in the lack of a suitable construct override, fails to compile.

So double[3] is not EmplaceConstruble from a std::initalizer_list<double> nor from a double[3] nor anything else (as the error occurs because I used a bracket, not because of what was in the brackets, in the placement new), unless the allocator does magic I am not aware of to avoid the placement new.

Thus your code violates the current draft standard, and probably C++11, and certainly C++03 (which had stricter requirements on containers).

answered Aug 15, 2014 at 16:57
Sign up to request clarification or add additional context in comments.

7 Comments

May be I am missing something essential here, but analogously, why doesn't compiler complain if I code std::vector<double[3]> arr; arr[0][0] = 1;
@Abhijit Undefined behaviour; the compiler can do anything, including not complaining.
@AlanStokes: I don;t think so Yakk mentioned that its an UB, but then I would like to hear back from Yakk.
C++11 removed the global requirements on container template parameters, and now specifies requirements on the individual container functions that need those behaviors from their elements.
@Abhijit answer revised with the current draft standard instead of my memory of the C++03 standard.
|
3

This is a bug in gcc and MSVC; clang compiles your code correctly.

Recent versions of gcc actually crash ("ice") the compiler:

internal compiler error: tree check: expected class ‘type’, have ‘exceptional’ (error_mark) in useless_type_conversion_p, at tree-ssa.c:1189

The standard is reasonably clear; from [dcl.init.list]:

5 - An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated an array of N elements of type E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list<E> object is constructed to refer to that array. [...]

Adapting the example from that paragraph:

using E = double[3];
using X = std::vector<E>;
E __a[2] = {{10, 11, 12}, {20, 21, 22}};
X x(__a, __a+2);

This is a bit of a cheat, though; a closer translation would write E __a[2] = {E{10, 11, 12}, E{20, 21, 22}};, which is not valid. But it certainly is possible to copy-initialize an array double[3] from a braced-init-list: E __a0 = {10, 11, 12};

answered Aug 15, 2014 at 17:01

5 Comments

This is a bug in gcc and MSVC. Is this a documented bug? Also, I am quite not sure if [dcl.init.list] justifies the syntax. Moreover, I think, just because clang compiles, we can call it a bug (but its just what I think and I may be wrong :-) )
Hmm. If we wrap the inner braced-init-lists with E then we get the same error as gcc. But it certainly is possible to copy-initialize an array double[3] from a braced-init-list: E __a0 = {10, 11, 12}.
@Abhijit: Clang has bugs too. You can't just assume something is a bug in GCC simply because it compiles in Clang.
It isn't about constructing double[3] from a braced initializer list (although that is one step), you also need emplace constructible from double[3]. And I don't think it is.
double[3] can't even legally be a container's value_type because it is not even Erasable. The specification results in a pseudo-destructor call, which isn't well-formed since pseudo-destructor calls are allowed for scalar types only.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.