I'm trying to use auto
to automatically deduce the type of a nested std::initializer_list
.
auto list = {
{{ 0, 1}, { 2, 3 }},
{{ 4, 5}, { 6, 7 }},
};
The actual type here is std::initializer_list<std::initializer_list<std::initializer_list<int>>>
, but when I attempt to compile it I get an error stating that auto
cannot deduce the type. Is there any way to get auto
to recognize such a construct?
I have a program where these initializer lists could be of arbitrary sizes and depths so hardcoding the types is not practical.
I found this documentation about initializer lists here: https://en.cppreference.com/w/cpp/language/list_initialization
A braced-init-list is not an expression and therefore has no type, e.g.
decltype({1,2})
is ill-formed. Having no type implies that template type deduction cannot deduce a type that matches a braced-init-list, so given the declarationtemplate<class T> void f(T);
the expressionf({1,2,3})
is ill-formed. However, the template parameter can otherwise be deduced, as is the case forstd::vector<int> v(std::istream_iterator<int>(std::cin), {})
, where the iterator type is deduced by the first argument but also used in the second parameter position. A special exception is made for type deduction using the keyword auto , which deduces any braced-init-list asstd::initializer_list
in copy-list-initialization.
The documentation seems to suggest that there is a special exception made for type deduction using auto
so you would think that this would work... But it seems that when you use a nested list auto
cannot deduce the type.
I have a program where these initializer lists could be of arbitrary sizes and depths so hardcoding the types is not practical.
Then you need to fix that problem.
You should not think of braced-init-lists as a quick-and-dirty way of making arrays of values without having to think about their types. That's not their purpose. Their purpose is to initialize values. The type std::initializer_list
is intended to be an intermediary phase in the process of initializing some type (which is why constructors that take a single initializer_list
are given special meaning in list initialization).
If you want to have arrays of arrays of arrays of various depths and such, then you're going to need to figure out which type that construct needs to be and type it out. auto
can only deduce a single level of braced-init-list; you'll need to specify the type(s) explicitly if you need deeper levels.
there is a special exception made for type deduction using
auto
Yes, there is. But it only applies to deducing a list for auto
itself, not for anything that auto
's deduction would require.
In order for auto list = {{1, 2, 3}};
to work, the compiler has to deduce two types: the type to be used for {1, 2, 3}
and the type to be used for list
. The deduction of the type for list
requires deducing the type for the nested braced-init-list. But you can't deduce the type of a braced-init-list. Hence it does not work.
It should also be noted that, even if it did work, it wouldn't actually work. The reason being that the initializer_list
inside of list
would refer to a temporary array. A temporary array that would be destroyed at the end of the initializing expression. It's basically the same reason why string_view sv = std::string("foo");
doesn't produce something useful.