展开到 std::tuple 的可变参数模板
Variadic template unrolling to std::tuple
我有一个过滤器 class,它接受两个模板参数,即输入数和输出数。
template<int Ins, int Outs>
class Filter
{
// implementation
};
有时我需要串联多个过滤器,所以我想将它们包装在 class
template<int... args>
class Chain
{
};
所以当我使用链条时
Chain<5, 10, 25, 15> chain;
它将 args 展开成一个元组,最终在链中得到类似这样的东西 class
std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;
这样的事情可能吗?我对这些概念还很陌生,无法理解它。
我已经遇到过类似的问题,最后得到了一个运算符* for class 过滤器接受一个 Filter<Ins1, Ins>
对象并构建一个 Filter<Ins1, Outs>
.
template<int Ins, int Outs>
class Filter
{
template <int Ins1>
Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
{
// implementation
}
};
现在的问题是:您的过滤器有什么作用?组合是否可能(也许我有偏见,因为在我的上下文中 Filter 是一个函数,而在我的例子中 operator* 是函数组合)
我们可以使用一些递归模板魔术来做到这一点:
//helper class template which will handle the recursion
template <int... Args>
struct FiltersFor;
//a helper to get the type of concatenating two tuples
template <typename Tuple1, typename Tuple2>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
std::declval<Tuple2>()));
//pop off two ints from the pack, recurse
template <int Ins, int Outs, int... Others>
struct FiltersFor<Ins,Outs,Others...>
{
//the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>,
typename FiltersFor<Outs,Others...>::type>;
};
//base case, 1 int left
template <int Dummy>
struct FiltersFor<Dummy>
{
using type = std::tuple<>;
};
//for completeness
template <>
struct FiltersFor<>
{
using type = std::tuple<>;
};
//our front-end struct
template<int... args>
using Chain = typename FiltersFor<args...>::type;
或者,我们可以摆脱单一 int 和非 int 版本,并像这样定义主模板:
template <int... Args>
struct FiltersFor
{
using type = std::tuple<>;
};
现在我们可以这样测试了:
static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
我们可以用三行代码完成这个,不用递归:
template<int... args>
struct Chain
{
// put args... into a constexpr array for indexing
static constexpr int my_args[] = {args...};
// undefined helper function that computes the desired type in the return type
// For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
// expands to Filter<my_args[0], my_args[1]>,
// Filter<my_args[1], my_args[2]>, ...,
// Filter<my_args[N-2], my_args[N-1]>
template<size_t... Is>
static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
helper(std::index_sequence<Is...>);
// and the result
using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
};
Demo.
我有一个过滤器 class,它接受两个模板参数,即输入数和输出数。
template<int Ins, int Outs>
class Filter
{
// implementation
};
有时我需要串联多个过滤器,所以我想将它们包装在 class
template<int... args>
class Chain
{
};
所以当我使用链条时
Chain<5, 10, 25, 15> chain;
它将 args 展开成一个元组,最终在链中得到类似这样的东西 class
std::tuple<Filter<5, 10>, Fiter<10, 25>, Filter<25, 15>> filters;
这样的事情可能吗?我对这些概念还很陌生,无法理解它。
我已经遇到过类似的问题,最后得到了一个运算符* for class 过滤器接受一个 Filter<Ins1, Ins>
对象并构建一个 Filter<Ins1, Outs>
.
template<int Ins, int Outs>
class Filter
{
template <int Ins1>
Filter<Ins1, Outs> operator*(const Filter<Ins1, Ins> &rhs) const
{
// implementation
}
};
现在的问题是:您的过滤器有什么作用?组合是否可能(也许我有偏见,因为在我的上下文中 Filter 是一个函数,而在我的例子中 operator* 是函数组合)
我们可以使用一些递归模板魔术来做到这一点:
//helper class template which will handle the recursion
template <int... Args>
struct FiltersFor;
//a helper to get the type of concatenating two tuples
template <typename Tuple1, typename Tuple2>
using tuple_cat_t = decltype(std::tuple_cat(std::declval<Tuple1>(),
std::declval<Tuple2>()));
//pop off two ints from the pack, recurse
template <int Ins, int Outs, int... Others>
struct FiltersFor<Ins,Outs,Others...>
{
//the type of concatenating a tuple of Filter<Ins,Outs> with the tuple from recursion
using type = tuple_cat_t<std::tuple<Filter<Ins,Outs>>,
typename FiltersFor<Outs,Others...>::type>;
};
//base case, 1 int left
template <int Dummy>
struct FiltersFor<Dummy>
{
using type = std::tuple<>;
};
//for completeness
template <>
struct FiltersFor<>
{
using type = std::tuple<>;
};
//our front-end struct
template<int... args>
using Chain = typename FiltersFor<args...>::type;
或者,我们可以摆脱单一 int 和非 int 版本,并像这样定义主模板:
template <int... Args>
struct FiltersFor
{
using type = std::tuple<>;
};
现在我们可以这样测试了:
static_assert(std::is_same<Chain<1,2,3,4>, std::tuple<Filter<1,2>,Filter<2,3>,Filter<3,4>>>::value, "wat");
static_assert(std::is_same<Chain<1,2>, std::tuple<Filter<1,2>>>::value, "wat");
static_assert(std::is_same<Chain<>, std::tuple<>>::value, "wat");
我们可以用三行代码完成这个,不用递归:
template<int... args>
struct Chain
{
// put args... into a constexpr array for indexing
static constexpr int my_args[] = {args...};
// undefined helper function that computes the desired type in the return type
// For Is... = 0, 1, ..., N-2, Filter<my_args[Is], my_args[Is+1]>...
// expands to Filter<my_args[0], my_args[1]>,
// Filter<my_args[1], my_args[2]>, ...,
// Filter<my_args[N-2], my_args[N-1]>
template<size_t... Is>
static std::tuple<Filter<my_args[Is], my_args[Is+1]>...>
helper(std::index_sequence<Is...>);
// and the result
using tuple_type = decltype(helper(std::make_index_sequence<sizeof...(args) - 1>()));
};
Demo.