Suppose I have a function T foo(size_t i). What would be an elegant and succinct way of constructing an object arr, of type std::array<T, N>, so that we have arr[i] == foo(i)?
If possible, I would like for this construction to work even when T is not a default-initializable type.
Notes:
T is not default-initializable, the code can't start with std::array<T, N> arr; and then some initialization.N, generically.I suggest the idiom of writing a utility function template which does this for you, succinctly and clearly:
template<std::size_t N, typename Generator>
constexpr auto generate_array(Generator&& gen)
-> std::array<decltype(gen(0), N>;
so, the generate_array function takes the generator as a parameter, and the number of elements to generate as a template-parameter.
If you write:
auto gen = [](std::size_t i) { return 11*(i+1); };
auto arr = generate_array<5>(gen);
for (auto i : arr) { std::cout << i << ' ';}
std::cout << '\n';
the program will print:
11 22 33 44 55
and you can see this in action on GodBolt.
We will use a helper functions involving some template metaprogramming voodoo known as the "indices trick":
namespace detail {
template<typename T, std::size_t... Is, typename Generator>
auto generate_array(std::index_sequence<Is...>, Generator&& gen)
-> std::array<T, sizeof...(Is)>
{
return std::array<T, sizeof...(Is)>{ gen(Is) ... };
}
} // namespace detail
template<std::size_t N, typename Generator>
constexpr auto generate_array(Generator&& gen) ->
std::array<decltype(gen(0)), N>
{
return detail::generate_array<decltype(gen(0))>(
std::make_index_sequence<N>{}, std::forward<Generator>(gen));
}
the inner function (in detail::) has a template parameter pack of the same size as the intended array, so it can use pack expansion to get just the right number of elements you need for your output; the external function creates that pack.
std::index_sequence and its being constexpr; if you implement it yourself in C++11, you can adapt the generator function and have a pure-C++11 solution.detail::generate_array() function template, as @Jarod42 points out. This is explicitly demonstrated in @TobySpeight's answer.N of 500,000 may well fail.std::array of constant values.size_t (in case the generator behaves differently on int's and on size_t's - which it might), and that the function can be marked constexpr.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