(Note: This question is about not having to specify the number of elements and still allow nested types to be directly initialized.)
This question discusses the uses left for a C array like int arr[20];
. On his answer, @James Kanze shows one of the last strongholds of C arrays, it's unique initialization characteristics:
int arr[] = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 };
We don't have to specify the number of elements, hooray! Now iterate over it with the C++11 functions std::begin
and std::end
from <iterator>
(or your own variants) and you never need to even think of its size.
Now, are there any (possibly TMP) ways to achieve the same with std::array
? Use of macros allowed to make it look nicer. :)
??? std_array = { "here", "be", "elements" };
Edit: Intermediate version, compiled from various answers, looks like this:
#include <array>
#include <utility>
template<class T, class... Tail, class Elem = typename std::decay<T>::type>
std::array<Elem,1+sizeof...(Tail)> make_array(T&& head, Tail&&... values)
{
return { std::forward<T>(head), std::forward<Tail>(values)... };
}
// in code
auto std_array = make_array(1,2,3,4,5);
And employs all kind of cool C++11 stuff:
- Variadic Templates
sizeof...
- rvalue references
- perfect forwarding
std::array
, of course- uniform initialization
- omitting the return type with uniform initialization
- type inference (
auto
)
And an example can be found here.
However, as @Johannes points out in the comment on @Xaade's answer, you can't initialize nested types with such a function. Example:
struct A{ int a; int b; };
// C syntax
A arr[] = { {1,2}, {3,4} };
// using std::array
??? std_array = { {1,2}, {3,4} };
Also, the number of initializers is limited to the number of function and template arguments supported by the implementation.
Best I can think of is:
However, this requires the compiler to do NRVO, and then also skip the copy of returned value (which is also legal but not required). In practice, I would expect any C++ compiler to be able to optimize that such that it's as fast as direct initialization.
Create an array maker type.
It overloads
operator,
to generate an expression template chaining each element to the previous via references.Add a
finish
free function that takes the array maker and generates an array directly from the chain of references.The syntax should look something like this:
It does not permit
{}
based construction, as onlyoperator=
does. If you are willing to use=
we can get it to work:or
None of these look like good solutions.
Using variardics limits you to your compiler-imposed limit on number of varargs and blocks recursive use of
{}
for substructures.In the end, there really isn't a good solution.
What I do is I write my code so it consumes both
T[]
andstd::array
data agnostically -- it doesn't care which I feed it. Sometimes this means my forwarding code has to carefully turn[]
arrays intostd::array
s transparently.C++11 will support this manner of initialization for (most?) std containers.
I'd expect a simple
make_array
.Using trailing return syntax
make_array
can be further simplifiedUnfortunatelly for aggregate classes it requires explicit type specification
In fact this
make_array
implementation is listed in sizeof... operatorc++17 version
Thanks to template argument deduction for class templates proposal we can use deduction guides to get rid of
make_array
helperCompiled with
-std=c++1z
flag under x86-64 gcc 7.0(Solution by @dyp)
Note: requires C++14 (
std::index_sequence
). Although one could implementstd::index_sequence
in C++11.