A somewhat elegant way to implement this operation is with a fold expression:
// Some namespace to hide the poorly-constrained template function:
namespace array_making {
template <std::size_t N>
struct array_dim {};
template <typename T, std::size_t N>
constexpr auto operator%(array_dim<N>, T const&)
-> std::array<T, N>;
}
template <typename T, std::size_t... Is>
using md_array_t = decltype(
(array_making::array_dim<Is>{} % ... % std::declval<T>())
);
Compiler Explorer.
Then md_array_t<int, 1, 2, 3>
is array<array<array<int, 3>, 2>, 1>
. If you prefer the opposite order, reverse the parameters of the operator%
and the arguments to the fold expression.
Note that this will run into problems if the type T
has an unconstrained operator%
in an associated namespace (please constrain your operators!). We can reduce the risk of this happening by choosing unlikely operators such as .*
, ->*
, or %=
; or we can use an array_type<T>
wrapper. Neither solution completely avoids the problem of improperly constrained operator overloads for T
.