Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template-defined number of template parameters (very meta)

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.

like image 267
Amras Avatar asked Aug 28 '20 11:08

Amras


People also ask

What is correct for template parameter?

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.)

What is meant by templates parameters?

A template parameter is a special kind of parameter that can be used to pass a type as argument.

How many parameters are legal for non-type template?

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.

Why do we use :: template template parameter?

8. Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.


2 Answers

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;
like image 108
Jarod42 Avatar answered Oct 02 '22 09:10

Jarod42


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.

like image 37
463035818_is_not_a_number Avatar answered Oct 02 '22 10:10

463035818_is_not_a_number