将模板参数转换为以逗号分隔的模板参数列表

时间:2019-03-21 13:03:23

标签: c++17 eigen3

如果标题有误导性,或者以前已经回答过此问题,则表示歉意。

我正在使用Eigen的Tensor模块,尤其是Eigen::TensorFixedSize类,因为我知道在编译时的形状。

本质上,因为这是一个洛伦兹问题,所以秩2张量会像这样,

Eigen::TensorFixedSize<double, Eigen::Sizes<4,4>> t;

3级张量,

Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4>> t;

以此类推。

我想写一个能够根据等级初始化张量的类。用伪代码,

template<typename RANK>
 class Foo
 {
  public:
   ...
  private:
   Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4,...,RANK times>> _t;
 }

以某种方式从

转换模板参数

<2> --> <4,4>

<3> --> <4,4,4>

最多<N>中的任意无符号整数。

这可能吗?

2 个答案:

答案 0 :(得分:3)

是的。

template <class RankIdx>
struct TensorForRank;

template <std::size_t... RankIdx>
struct TensorForRank<std::index_sequence<RankIdx...>> {
    using type = Eigen::TensorFixedSize<double, Eigen::Sizes<(void(RankIdx), 4)...>>;
};

template <std::size_t Rank>
using TensorForRank_t = typename TensorForRank<std::make_index_sequence<Rank>>::type;

用作:

template<std::size_t Rank>
class Foo
{
    // ...
private:
    TensorForRank_t<Rank> _t;
};

See it live on Wandbox(带有占位符test<...>模板,因为Eigen不可用)

答案 1 :(得分:1)

Quentin's answer非常好,我会喜欢的。

唯一的缺点是索引序列[0,1,2,...]的“无用”生成,我们忽略了它的值并替换了我们自己的值。

如果我们想直接创建重复的值,我们可以编写自己的生成器代码(更为冗长):

首先通过别名std::integer_sequence,创建一个可以容纳多个std::size_t值的类型:

template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;

目标是最终创建一个value_sequence<4, 4, 4>,然后使用这4s实例化一个Eigen::Sizes

接下来我们需要做的是连接两个序列,因为我们将像这样构建它:

concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>

我们可以通过存根方法执行此操作,该存根方法接受两种value_sequence类型并返回串联的结果。请注意,我们从未为此方法编写定义。我们只是利用类型系统来编写比模板专业化所需要的代码少的代码:

template<std::size_t... lhs, std::size_t... rhs>
constexpr auto concat(value_sequence<lhs...>, value_sequence<rhs...>) -> value_sequence<lhs..., rhs...>;

这时我们有足够的机制来创建value_sequence<4,4,4>,所以现在我们需要一种方法来指示我们希望使用的值(4)和重复生成它的次数(3) :

template<std::size_t value, std::size_t num_repeats>
struct repeated_value
{
    using left_sequence = value_sequence<value>;
    using right_sequence = typename repeated_value<value, num_repeats-1>::type;
    using type = decltype(concat(left_sequence{}, right_sequence{}));
};

repeated_value<4, 3>::type产生一个value_sequence<4, 4, 4>

由于repeated_value<...>::type是递归的,因此我们需要通过部分专业化来提供基本情况:

template<std::size_t value>
struct repeated_value<value, 1>
{
    using type = value_sequence<value>;
};

太好了。剩下的就是让我们接收一个Eigen::Sizes类和一个value_sequence<4, 4, 4>类型,并产生Eigen::Sizes<4, 4, 4>

我们可以再次使用部分模板专门化来做到这一点:

template<template<std::size_t...> class T, class...>
struct InstantiateWithRepeatedVals;

template<template<std::size_t...> class T, std::size_t... vals>
struct InstantiateWithRepeatedVals<T, value_sequence<vals...>> 
{
    using type = T<vals...>;
};

那!投入一些助手以使其更容易使用,我们完成了:

template<std::size_t value, std::size_t num_repeats>
using repeated_value_t = typename repeated_value<value, num_repeats>::type;

template<template<std::size_t...> class T, std::size_t Value, std::size_t N>
using InstantiateWithRepeatedVals_t = typename InstantiateWithRepeatedVals<T, repeated_value_t<Value, N>>::type;

现在我们可以像这样使用它了:

using my_type = InstantiateWithRepeatedVals_t<EigenSizes, 4, 3>;
static_assert(std::is_same_v<my_type, EigenSizes<4, 4, 4>>);

Live Demo