扩展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> ;
template <int... Indices>
struct indices<0, Indices...>
;
template <int Index, int... Indices>
struct indices<Index, Indices...>
;
template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
如果您有一个函数模板,假设它叫做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::tuple<...>
元素和
std::forward<...>()
的方法。仅仅使用
std::forward<Tuple>(t)
是
不行的,因为它可能会移动整个
std::tuple<...>
,而非移动元素。我
认为可以进行适当的逐个元素移动
std::tuple<...>
,但我还没有这样做过。
get<0>(t), get<1>(t), get<2>(t), ..., get<N>(t)
。 - Xeostd::forward
是完全可以的,因为它只提取元组元素,因此只会移动它们。如果你知道自己在做什么,我不认为这是一个反模式,也不认为存在问题,就像你所说的那样。 - Xeoget(tuple<Types...>&& t)
的效果等同于return std::forward<typename tuple_element<I, tuple<Types...> >::type&&>(get<I>(t));
。因此,元组并没有实际上被移动,只有包含的元素被转发(forward
本身是指定为一个static_cast
)。 - dypstd::move
不移动,而std::forward<>
不转发_” :) - seheget
根据参数的 rvalueness 和元组中存储的类型,返回一个 lvalue 或 rvalue 引用。如果元组存储了一个 lvalue 引用,即使将元组作为 rvalue 引用传递,我们也不应该移动它。只有当存储的类型是 rvalue 引用时,std::get<N>(t)
才会返回一个 rvalue 引用,但在存储了 lvalue 引用或非引用类型的情况下,它将返回一个 lvalue 引用。 - dyp