I have a function f(i, j) that accepts two std::size_t arguments and I want to make a constexpr std:array out of its values on a 2d grid, (i = 0, ..., rows-1; j = 0, ..., cols-1) in, say, column-major order.
The code:
#include <array>
#include <tuple>
#include <type_traits>
template<std::size_t rows, std::size_t cols>
constexpr auto make_2d_index_table()
{
std::array<std::array<std::size_t, 2>, rows * cols> table{};
for (std::size_t i = 0; i < table.size(); ++i)
table[i] = {i % rows, i / rows};
return table;
}
template<std::size_t rows, std::size_t cols, class Func>
constexpr std::array<std::invoke_result_t<Func, std::size_t, std::size_t>, rows * cols>
make_array_of_2d_func_values(Func&& func)
{
return apply([&func](auto... row_col)
{
return std::array<std::invoke_result_t<Func, std::size_t, std::size_t>, rows * cols>
{std::apply(std::forward<Func>(func), row_col)...};
}, make_2d_index_table<rows, cols>());
}
Usage:
constexpr auto arr = make_array_of_2d_func_values<5, 3>(
[](auto i, auto j) { return i + 2 * j; });
The object of the type returned by a function may not be default constructible. So I can't just create an array<invoke_result_t<...>, rows * cols> arr{} of default constructed objects and then initialize it in the for loop like in make_index_table().
Is this a good solution?