所以我想弄清楚它是如何工作的:C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?
我不明白的黑魔法是这段代码片段:
f(std::get<N>(std::forward<Tuple>(t))...)
这是我不理解的f
内的表达。
我理解表达式以某种方式将t
内的内容解压缩/扩展为参数列表。但有人可以解释如何做到这一点?当我查看std::get
(http://en.cppreference.com/w/cpp/utility/tuple/get)的定义时,我看不出N
如何适合......?据我所知,N是一个整数序列。
根据我可以观察到的内容,我假设E<X>...
形式的表达式X
是类型X1
的序列。 X2
,... Xn
,表达式将扩展为E<X1>, E<X2> ... E<Xn>
。这是它的工作原理吗?
编辑:在这种情况下,N不是类型序列,而是整数。但我猜这种语言结构适用于类型和价值观。
答案 0 :(得分:6)
我认为@ Xeo的评论总结得很好。从C ++ 11标准的14.5.3开始:
包扩展包含模式和省略号, 其实例化产生零个或多个实例化 列表中的模式。
在你的情况下,当你完成递归模板实例化并最终进入部分特化时,你有
f(std::get<N>(std::forward<Tuple>(t))...);
...其中N
是四个int
s 0
,1
,2
和3
的参数包。从上面的标准来看,这里的模式是
std::get<N>(std::forward<Tuple>(t))
将...
省略号应用于上述模式使其以列表形式扩展为四个实例,即
f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
答案 1 :(得分:2)
扩展std::tuple<T...>
的基本要素实际上是从代码中省略的:你需要获得第二个参数:除了std::tuple<...>
的类型列表之外,还需要一个参数包指数0, 1, ..., n
。一旦有了这两个参数包,就可以将它们串联扩展:
template <typename F, typename... T, int... N>
void call_impl(F&& fun, std::tuple<T...>&& t) {
fun(std::get<N>(t)...);
}
当你拥有std::tuple<T...>
时,真正的魔力在于召唤第二个参数包。它需要一些模板编程。以下是创建索引列表的方法:
template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
typedef typename indices<Index - 1, Index, Indices...>::type type;
};
template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
{
return 0;
}
所以,如果你有一个函数模板,让我们称它为call()
,它带有一个函数对象,一个std::tuple<T...>
带有函数的参数。一种简单的方法是重写上面提到的call_impl()
来处理推断索引:
template <typename F, typename Tuple, int... N>
void call_impl(F&& fun, Tuple&& t, indices<Indices...> const*)
{
fun(std::get<N>(t)...);
}
template <typename F, typename Tuple>
void call(F&& fun, Tuple&& t)
{
call_imle(std::forward<F>(fun), std::forward<Tuple>(t), make_indices<Tuple>());
}
这段代码没有真正扩展的是调用函数时std::forward<...>()
与各种std::tuple<...>
元素的正确使用。只使用std::forward<Tuple>(t)
不,因为它可能会移动整个std::tuple<...>
而不是移动元素。我认为类似于std::tuple<...>
的合适元素移动的东西可以完成,但我还没有完成它。