(Note: This question is about not having to specify the number of elements and still allow nested types to be directly initialized.)
This question discu
I know it's been quite some time since this question was asked, but I feel the existing answers still have some shortcomings, so I'd like to propose my slightly modified version. Following are the points that I think some existing answers are missing.
Some answers mention that we need to rely on RVO to return the constructed array. That is not true; we can make use of copy-list-initialization to guarantee there will never be temporaries created. So instead of:
return std::array{values};
we should do:
return {{values}};
make_array a constexpr functionThis allow us to create compile-time constant arrays.
First off, if they are not, the compiler will issue a warning or error anyway because list-initialization doesn't allow narrowing. Secondly, even if we really decide to do our own static_assert thing (perhaps to provide better error message), we should still probably compare the arguments' decayed types rather than raw types. For example,
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
If we are simply static_asserting that a, b, and c have the same type, then this check will fail, but that probably isn't what we'd expect. Instead, we should compare their std::decay_t types (which are all ints)).
This is similar to point 3. Using the same code snippet, but don't specify the value type explicitly this time:
volatile int a = 0;
const int& b = 1;
int&& c = 2;
auto arr = make_array(a, b, c); // Will this work?
We probably want to make an array, but the implementations in the existing answers probably all fail to do that. What we can do is, instead of returning a std::array, return a std::array.
There is one disadvantage about this approach: we can't return an array of cv-qualified value type any more. But most of the time, instead of something like an array, we would use a const array anyway. There is a trade-off, but I think a reasonable one. The C++17 std::make_optional also takes this approach:
template< class T >
constexpr std::optional> make_optional( T&& value );
Taking the above points into account, a full working implementation of make_array in C++14 looks like this:
#include
#include
#include
template
constexpr std::array, 1 + sizeof... (Ts)>
make_array(T&& t, Ts&&... ts)
noexcept(noexcept(std::is_nothrow_constructible<
std::array, 1 + sizeof... (Ts)>, T&&, Ts&&...
>::value))
{
return {{std::forward(t), std::forward(ts)...}};
}
template
constexpr std::array, 0> make_array() noexcept
{
return {};
}
Usage:
constexpr auto arr = make_array(make_array(1, 2),
make_array(3, 4));
static_assert(arr[1][1] == 4, "!");