[英]Variadic templates: producing a tuple of pairs of adjacent elements
My goal is to do something so that for instance, 我的目标是做一些事情,例如,
pairs<1,2,3,4>()
Has return type 有退货类型
std::tuple<some_other_type<1,2>, some_other_type<2,3>, some_other_type<3,4>>
I am wondering if this is even possible with C++ template metaprogramming, and how it could be accomplished. 我想知道这是否可能与C ++模板元编程,以及如何实现它。 For actually producing the value, it seems as though I can use tuple_cat to recursively concatenate to the output, but I'm finding it difficult to express the return type, since it is itself variadic and effectively a function of the number of template parameters.
为了实际生成该值,似乎我可以使用tuple_cat递归地连接到输出,但我发现很难表达返回类型,因为它本身是可变参数并且实际上是模板参数数量的函数。 Complicating the situation, if I went the tuple_cat route it seems like I would also have to overload the function to take a tuple to concatenate onto, and the concatenation would happen at runtime, not compile-time.
使情况复杂化,如果我去了tuple_cat路由,似乎我也必须重载函数以使元组连接到,并且连接将在运行时发生,而不是编译时。 Am I on a wild goose chase here?
我在野鹅追逐吗?
Here is a way of doing that. 这是一种方法。 Given your class template
some_other_type
: 给出你的类模板
some_other_type
:
template<int I, int J>
struct some_other_type { };
And given some machinery hidden in the detail
namespace: 并且给出了一些隐藏在
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()));
};
}
You could provide a simple function that instantiates the helper class template: 您可以提供一个实例化辅助类模板的简单函数:
template<int... Is>
typename detail::pairs<Is...>::type pairs()
{
return typename detail::pairs<Is...>::type();
}
And here is how you would use it (and a test case): 以下是您将如何使用它(以及测试用例):
#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!");
}
Finally, here is a live example . 最后,这是一个实例 。
IMPROVEMENT: ( why writing <1, 2, 3, 4>
when one could write <1, 5>
)? 改进:( 为什么写
<1, 2, 3, 4>
当一个人可以写<1, 5>
)?
It is also possible to extend the above solution so that it won't be required to manually write every number between the minimum and the maximum as a template argument of pairs()
. 还可以扩展上述解决方案,以便不需要手动将最小值和最大值之间的每个数字写为
pairs()
的模板参数。 Given the additional machinery below, again hidden in a detail
namespace: 鉴于下面的附加机制,再次隐藏在
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...>();
}
}
It is possible to define a helper function pairs_range()
which accepts 2 template arguments defining the range [begin, end)
- where end
is not included, in the style of the Standard Library: 可以定义一个辅助函数
pairs_range()
,该函数接受2个模板参数,这些参数定义范围[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>());
}
And this is how one would use it (including a test case): 这就是人们如何使用它(包括测试用例):
int main()
{
// Won't fire!
static_assert(
std::is_same<
decltype(pairs_range<1, 5>()),
decltype(pairs<1, 2, 3, 4>())
>::value,
"Error!");
}
And once again, here is a live example . 再一次,这是一个现实的例子 。
Here is my version of it ( live here ), 100% compile-time, returning the new parameter list as a type (not a function return): 这是我的版本( 在这里 ),100%编译时,将新参数列表作为类型返回(不是函数返回):
First, let's define our result structures: 首先,让我们定义我们的结果结构:
template<int a, int b>
struct tpair
{
};
template<typename... p>
struct final_
{
};
The key point is to concat parameters packs. 关键点是连接参数包。 Here is the struct that will do the job:
这是将完成工作的结构:
template<typename a, typename b>
struct concat
{
};
template<typename a, typename... b>
struct concat<a, final<b...>>
{
typedef final_<a,b...> type;
};
Now, the struct used to 'pairize' your list. 现在,结构用于“配对”您的列表。 Normally it will fail with odd numbers of values:
通常,它将以奇数个值失败:
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;
};
In the live example there is also a code outputting the name of all types in a parameter pack to the console, with demangling, as a test (was funnier to use than the incomplete type trick) . 在实例中,还有一个代码将参数包中所有类型的名称输出到控制台,并使用demangling作为测试(使用比不完整类型技巧更有趣) 。
And now, let's try it with indices
and without recursion (except, of course, for the indices): 现在,让我们用
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" );
}
(OK, I cheated a bit: std::tuple_cat
might be recursive itself ;) (好吧,我作弊了一下:
std::tuple_cat
本身可能是递归的;)
Update: OK, I should have read the question more carefully. 更新:好的,我应该更仔细地阅读这个问题。 Here's the version which produces the desired result (
indices
/ make_indices
as above): 这是产生所需结果的版本(如上所述
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" );
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.