问题描述
template<int Degree> using curve = // some deFinition
template<int MaxDegree> using curve_variant = std::variant<curve<1>,curve<2>,.. curve<MaxDegree>>;
在上面的示例中,传递给std :: variant模板的参数数量将根据curve_variant的参数而变化:
例如,curve_variant<4>
将解析为std::variant<curve<1>,curve<3>,curve<4>>
。
由于MaxDegree在编译时是已知的,因此这是有可能的。但是我也不知道如何开始执行此操作。
解决方法
借助std::integer_sequence
助手,您可以这样做:
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;
,
其他答案表明std::integer_sequence
是一个很好的工具。假设我们没有。
以下内容仅是说明如果没有std::integer_sequence
,我们将必须编写哪些代码。实际上,没有理由用这种方式编写它,如果没有C ++ 14,则可以很容易地重新实现。
#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);
}
要实例化Foo<N>
(对不起,此名称)N
,递归是相当昂贵的,即使我们从未要求过其他类型也必须实例化。 std::integer_sequence
可以完全避免递归。
#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,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<3>,curve<4>>>);
}
以上仅适用于非负值,因此您最好使用std::size_t
,它是索引的自然类型。
只需回答其他答案,如果您拥有C ++ 11但没有C ++ 14,则可以使用一个巧妙的技巧来模拟std :: integer_sequence:
template <int...>
struct seq { };
template <int N,int... S>
struct gens : gens<N - 1,N - 1,S...> { };
template <int... S>
struct gens<0,S...> {
typedef seq<S...> type;
};
在这种模式下,gens
的运行方式与integer_sequence
相同。 gens<N>::type
是seq<1,2,3,...,N>
。因此,您使用它的方式相同
template <typename T>
struct make_curve_variant_impl
template <int... N>
struct make_curve_variant_impl<seq<N...>>
{
typef std::variant<curve<N+1>...> type;
};
template <typename N>
struct make_curve_variant
{
typedef make_curve_variant_impl<typename gens<N>::type>:: type;
};