可变模板:生成由相邻元素对组成的元组
Variadic templates: producing a tuple of pairs of adjacent elements
我的目标是做一些事情,例如,
pairs<1,2,3,4>()
有返回类型
std::tuple<some_other_type<1,2>, some_other_type<2,3>, some_other_type<3,4>>
我想知道这是否甚至可能与c++模板元编程,以及它是如何完成的。对于实际生成值,我似乎可以使用tuple_cat递归地连接到输出,但是我发现很难表达返回类型,因为它本身是可变的,并且实际上是模板参数数量的函数。使情况更加复杂的是,如果使用tuple_cat路由,似乎还必须重载函数以获取要连接的元组,并且连接将在运行时发生,而不是在编译时发生。我是在白费力气吗?
这里有一种方法。给定你的类模板some_other_type
:
template<int I, int J>
struct some_other_type { };
并给出隐藏在detail
命名空间中的一些机制:
namespace detail
{
template<int... Is>
struct pairs { };
template<int I, int J>
struct pairs<I, J>
{
using type = std::tuple<some_other_type<I, J>>;
};
template<int I, int J, int... Is>
struct pairs<I, J, Is...>
{
using type = decltype(std::tuple_cat(
std::tuple<some_other_type<I, J>>(),
typename pairs<J, Is...>::type()));
};
}
您可以提供一个简单的函数来实例化helper类模板:
template<int... Is>
typename detail::pairs<Is...>::type pairs()
{
return typename detail::pairs<Is...>::type();
}
下面是你如何使用它(和一个测试用例):
#include <type_traits>
int main()
{
auto p = pairs<1, 2, 3, 4>();
// Won't fire!
static_assert(
std::is_same<
decltype(p),
std::tuple<
some_other_type<1,2>,
some_other_type<2,3>,
some_other_type<3,4>>
>::value,
"Error!");
}
最后,这里是一个实际的例子。
改进: (可以写<1, 5>
为什么要写<1, 2, 3, 4>
)
也可以扩展上面的解决方案,这样就不需要手动编写最小值和最大值之间的每个数字作为pairs()
的模板参数。考虑到下面的附加机制,同样隐藏在detail
名称空间中:
namespace detail
{
template <int... Is>
struct index_list { };
template <int MIN, int N, int... Is>
struct range_builder;
template <int MIN, int... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};
template <int MIN, int N, int... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{ };
// Meta-function that returns a [MIN, MAX) index range
template<int MIN, int MAX>
using index_range = typename range_builder<MIN, MAX>::type;
template<int... Is>
auto pairs_range(index_list<Is...>) -> decltype(::pairs<Is...>())
{
return ::pairs<Is...>();
}
}
可以定义一个辅助函数pairs_range()
,它接受两个模板参数,定义范围[begin, end)
-其中不包括end
,以标准库的风格:
template<int I, int J>
auto pairs_range() -> decltype(pairs_range(detail::index_range<I, J>()))
{
return pairs_range(detail::index_range<I, J>());
}
下面是如何使用它的(包括一个测试用例):
int main()
{
// Won't fire!
static_assert(
std::is_same<
decltype(pairs_range<1, 5>()),
decltype(pairs<1, 2, 3, 4>())
>::value,
"Error!");
}
再一次,这是一个活生生的例子
这是我的版本(live Here), 100%编译时,返回新的参数列表作为类型(不是函数返回):
首先,让我们定义结果结构:template<int a, int b>
struct tpair
{
};
template<typename... p>
struct final_
{
};
关键是连接参数包。下面是完成这项工作的结构体:
template<typename a, typename b>
struct concat
{
};
template<typename a, typename... b>
struct concat<a, final<b...>>
{
typedef final_<a,b...> type;
};
现在,用于对列表进行"配对"的结构体。通常情况下,对于奇数的值,它将失败:
template<int a, int b, int... values>
struct pairize
{
// Choose one of the following versions:
// First version: only non-overlapping pairs : (1,2) (3,4) ...
typedef typename concat<tpair<a,b>, typename pairize<values...>::type>::type type;
// Second version: overlapping pairs : (1,2) (2,3) (3,4)...
typedef typename concat<tpair<a,b>, typename pairize<b,values...>::type>::type type;
};
template<int a, int b>
struct pairize<a,b>
{
typedef final_<tpair<a,b>> type;
};
在实际示例中,还有一段代码将参数包中所有类型的名称输出到控制台,使用demangling作为测试(使用比不完整类型技巧更有趣)。
现在,让我们尝试使用indices
并且不使用递归(当然,索引除外):
#include <tuple>
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 >
{
typedef indices<> type;
};
template< std::size_t, std::size_t >
struct sometype {};
template< typename, typename, typename >
struct make_pairs;
template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is >
struct make_pairs< indices< Ns... >, indices< Ms... >, indices< Is... > >
{
using type = decltype( std::tuple_cat(
std::declval< typename std::conditional< Is % 2 == 1,
std::tuple< sometype< Ns, Ms > >,
std::tuple<> >::type >()...
));
};
template< std::size_t... Ns >
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >,
typename make_indices< sizeof...( Ns ) + 1 >::type >::type;
int main()
{
static_assert( std::is_same< pairs<1,2,4,3,5,9>,
std::tuple< sometype<1,2>, sometype<4,3>, sometype<5,9> > >::value, "Oops" );
}
(好吧,我骗了一点:std::tuple_cat
本身可能是递归的;)
更新:好吧,我应该更仔细地阅读问题。下面是产生期望结果的版本(indices
/make_indices
如上所述):
template< std::size_t, std::size_t >
struct sometype {};
template< typename, typename, typename >
struct make_pairs;
template< std::size_t... Ns, std::size_t... Ms, std::size_t... Is >
struct make_pairs< indices< Ns... >, indices< Ms... >, indices< Is... > >
{
using type = decltype( std::tuple_cat(
std::declval< typename std::conditional< Is != 0 && Is != sizeof...( Is ) - 1,
std::tuple< sometype< Ns, Ms > >,
std::tuple<> >::type >()...
));
};
template< std::size_t... Ns >
using pairs = typename make_pairs< indices< 0, Ns... >, indices< Ns..., 0 >,
typename make_indices< sizeof...( Ns ) + 1 >::type >::type;
int main()
{
static_assert( std::is_same< pairs<1,2,3,4>,
std::tuple< sometype<1,2>, sometype<2,3>, sometype<3,4> > >::value, "Oops" );
}
- 在C++中,如何通过几种类型从元组中选择多个元素
- 如何基于元组元素进行递归?
- 如何从标准::元组中删除元素?
- 元组元素是只读的?
- c++:交换向量中所有元组的第一个和第二个元素
- 我无法在用forward_as_tuple创建的元组中按类型访问元素
- 将函数应用于元组中的每个元素,将每个元素强制转换为类型包中的不同类型,然后作为参数包传递
- 如何获取元组的元素
- 如何使用返回第 n 个元素的方法创建元组
- 用一个额外的元素扩展 std::array 的每个 std::元组
- 如何处理元组中的元素
- 在 Hana 中满足谓词的元组元素的索引序列
- 如何部分专业化功能以用元组元素作为参数调用功能
- 匹配部分专用化以按类型替换元组元素
- 编译按映射类型获取元素的映射错误元组
- 仅当元组中存在该类型时,将功能应用于元组元素
- 获取std ::元组元素作为std ::变体
- 获取对元组元素的引用
- 检查元组是否包含特定类型的元素
- 比较元组中的和分类元素