After some investigation, looks like the behavior initializing variables follows some convention.
For single element:
auto* x = new int; // undefined value
auto* x = new int{}; // 0 (default initializer)
auto* x = new int(23); // 23 (copy initializer)
auto* x = new Test; // default constructor
auto* x = new Test{}; // default constructor
auto* x = new Test(...); // X constructor (overload-chosen)
This makes a lot of sense, until you try to apply the same logic on arrays:
auto* x = new int[10]; // all undefined values - OK
auto* x = new int[10]{}; // all 0 (default initializer) - OK
auto* x = new int[10](23); // all 23 (copy initializer on all) - NOT IMPLEMENTED
auto* x = new Test[10]; // default constructors - OK
auto* x = new Test[10]{}; // default constructors - OK
auto* x = new Test[10](...); // X constructor on all (overload-chosen) - NOT IMPLEMENTED
My logic says that you can make some assumptions:
T(Args..)
then every element from the array can be constructed. There is no reason to not allow T[N](Args...)
syntax.Does anyone know why this feature doesn't exist? It would be very nice to allow
new int[10](23); // all 23
new Test[10]("asd", 123); // construct all using Test("asd", 123)
Edit: The whole idea of this is to avoid the default initialization/constructor and call the one we need directly.
While it would be nice if there was something for this in the standard library†, the functionality you want is relatively straightforward to implement:
namespace detail {
template<typename T, typename... ArgTs, std::size_t... Is>
constexpr auto make_filled_array(ArgTs const&... args, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, T(args...))...}};
}
}
template<typename T, std::size_t N, typename... ArgTs, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(ArgTs const&... args) {
return detail::make_filled_array<T, ArgTs...>(args..., std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<int, 10>(23);
auto arr2 = make_filled_array<Test, 10>("asd", 123);
Online Demo
That said, I don't see any point to taking constructor arguments here; container emplace
functions are useful because they do perfect-forwarding, but we can't do that here because we need to reuse the arguments for each constructor call (so moving from them is not an option). I think copy-construction is natural:
namespace detail {
template<typename T, std::size_t... Is>
constexpr auto make_filled_array(T const& t, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, t)...}};
}
}
template<std::size_t N, typename T, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(T const& t) {
return detail::make_filled_array(t, std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<10>(23);
auto arr2 = make_filled_array<10>(Test{"asd", 123});
Online Demo
N.b. your use of new
implies that you may be from a managed language and need to read up on value semantics. ;-] This code could be altered to return a std::array<T, N>*
or T*
(or even std::unique_ptr<T[]>
for something moderately reasonable), but, why would one..? Just use std::vector<T>
:
std::vector<int> vec(10, 23);
I chose to demonstrate std::array<>
here because your examples all have a constant size.
† The closest thing that comes to mind is std::fill
, but that wouldn't initialize the array...
I can't speak for the committee, but I can see a case where your syntax would be very very problematic: initializing from temporaries: .
X* x = new X[10](Y{});
How do you deal with this case?
You can't call X(Y&&)
(assuming it exists) for each of the 10 elements because you have only 1 temporary and all elements after the first would be initialized from an object in an unspecified state.
The other alternative would be to somehow call X(const Y&)
instead (assuming again it exists). Besides over complicating the standard and adding unexpected inconsistent behavior that just opens up another can of worms. The X
object could have a reference bind to our Y{}
assuming is an lvalue and you end up with a dangling reference.
The only viable solution I see is to not allow rvalue references on this syntax but that would just feed more SO questions.
I think it is pretty safe to assume the standard committee took this into consideration.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With