template<int Degree> using curve = // some definition
template<int MaxDegree> using curve_variant = std::variant<curve<1>, curve<2>, .. curve<MaxDegree>>;
In the above example, the number of parameters passed to std::variant's template would change depending on curve_variant's parameters:
For example, curve_variant<4>
would resolve to std::variant<curve<1>, curve<2>, curve<3>, curve<4>>
.
Since MaxDegree is known at compile time, this feels possible. But I also don't have a clue how to begin implementing this.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
A template parameter is a special kind of parameter that can be used to pass a type as argument.
The following are legal for non-type template parameters: integral or enumeration type, Pointer to object or pointer to function, Reference to object or reference to function, Pointer to member.
8. Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.
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.
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