将模板参数转换为 comma-separated 模板参数列表
Convert template parameter into comma-separated list of template parameters
如果标题具有误导性或此问题之前已回答,我们深表歉意。
我正在使用 Eigen 的 Tensor 模块,特别是 Eigen::TensorFixedSize
class,因为我知道编译时的形状。
本质上,因为这是一个洛伦兹问题,所以 2 阶张量会像,
Eigen::TensorFixedSize<double, Eigen::Sizes<4,4>> t;
一个三阶张量,
Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4>> t;
等等。
我想写一个 class 能够根据等级初始化张量。在 pseudo-code、
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>
中的任意无符号整数。
这样可以吗?
是的。
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 不可用)
非常好,我会去的。
唯一的缺点是 "useless" 索引序列 [0, 1, 2, ...] 的生成,我们忽略其值,并用我们自己的值代替。
如果我们想直接创建重复值,我们可以编写自己的生成器代码(比较冗长):
首先创建一个可以包含多个 std::size_t
值的类型,方法是给 std::integer_sequence
:
添加别名
template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;
目标是最终创建一个 value_sequence<4, 4, 4>
,然后使用这些 4 实例化一个 Eigen::Sizes
。
接下来我们需要做的是连接两个序列,因为我们要像这样构建它:
concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>
我们可以通过接受两个 value_sequence
类型和 returns 连接结果的存根方法来做到这一点。请注意,我们从未为该方法编写定义;我们只是利用类型系统编写的代码比模板特化所需要的代码更少:
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
class 和 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
如果标题具有误导性或此问题之前已回答,我们深表歉意。
我正在使用 Eigen 的 Tensor 模块,特别是 Eigen::TensorFixedSize
class,因为我知道编译时的形状。
本质上,因为这是一个洛伦兹问题,所以 2 阶张量会像,
Eigen::TensorFixedSize<double, Eigen::Sizes<4,4>> t;
一个三阶张量,
Eigen::TensorFixedSize<double, Eigen::Sizes<4,4,4>> t;
等等。
我想写一个 class 能够根据等级初始化张量。在 pseudo-code、
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>
中的任意无符号整数。
这样可以吗?
是的。
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 不可用)
唯一的缺点是 "useless" 索引序列 [0, 1, 2, ...] 的生成,我们忽略其值,并用我们自己的值代替。
如果我们想直接创建重复值,我们可以编写自己的生成器代码(比较冗长):
首先创建一个可以包含多个 std::size_t
值的类型,方法是给 std::integer_sequence
:
template<std::size_t... vals>
using value_sequence = std::integer_sequence<std::size_t, vals...>;
目标是最终创建一个 value_sequence<4, 4, 4>
,然后使用这些 4 实例化一个 Eigen::Sizes
。
接下来我们需要做的是连接两个序列,因为我们要像这样构建它:
concat(value_sequence<4>, value_sequence<4>) --> value_sequence<4, 4>
我们可以通过接受两个 value_sequence
类型和 returns 连接结果的存根方法来做到这一点。请注意,我们从未为该方法编写定义;我们只是利用类型系统编写的代码比模板特化所需要的代码更少:
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
class 和 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>>);