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::arrayinstead 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.
2 Answers 2
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).
7 Comments
std::vector<double[3]> arr; arr[0][0] = 1;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 ofNelements of typeE, whereNis 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 thestd::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};
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 :-) )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}.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.Explore related questions
See similar questions with these tags.
arr{{{ 10, 11,12}, {20, 21,22}}}?