Template-defined number of template parameters (very meta)
With std::integer_sequence
helper, you might do:
template <typename Seq> struct curve_variant_impl;
template <int ... Is>
struct curve_variant_impl<std::integer_sequence<int, Is...>>
{
using type = std::variant<curve<1 + Is>...>;
};
template <int MaxDegree>
using curve_variant = typename curve_variant_impl<std::make_integer_sequence<int, MaxDegree>>::type;
As the other answers show std::integer_sequence
is a nice tool. Suppose we didn't have it.
The following is only to illustrate what code we would have to write if we didn't have std::integer_sequence
. As a matter of fact, there is no reason to write it this way, if you do not have C++14, you can reimplement is easily.
#include <variant>
#include <type_traits>
template<int Degree> struct curve{};
// helper to add a type to a variant
template <typename A,typename... others>
struct merge_variants {
using type = std::variant<others...,A>;
};
template <typename A,typename... others>
struct merge_variants<A,std::variant<others...>> : merge_variants<A,others...> {};
// the recursion:
template <int MaxDegree>
struct Foo {
using type = typename merge_variants< curve<MaxDegree>,typename Foo<MaxDegree-1>::type >::type;
};
// the base case:
template <>
struct Foo<1> {
using type = std::variant< curve<1> >;
};
int main() {
static_assert(std::is_same<std::variant<curve<1>,curve<2>,curve<3>> , Foo<3>::type >::value);
}
Recursion is rather expensive, to instantiate Foo<N>
(sorry for the name) N
other types have to be instantiated, even though we never asked for them. std::integer_sequence
can avoid the recursion completely.
#include <utility>
#include <variant>
template<int Degree>
struct curve{};
template<typename index_seq>
struct curve_variant_impl;
template<int...indices>
// Start binding indices from 1, not zero
struct curve_variant_impl<std::integer_sequence<int,0,indices...>>{
using type = std::variant<curve<indices>...>;
};
template<int MaxDegree>
//make_integer_sequence makes [0,MaxDegree), we want [1,MaxDegree]
using curve_variant = typename curve_variant_impl<std::make_integer_sequence<int,MaxDegree+1>>::type;
int main() {
static_assert(std::is_same_v<curve_variant<4>,std::variant<curve<1>, curve<2>, curve<3>, curve<4>>>);
}
The above works only with non-negative values, so you might as well use std::size_t
which is natural type for indices.