2013-09-24 9 views
9

Так что я пытаюсь выяснить, как это работает: 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 не является последовательностью типов, а целыми числами. Но я предполагаю, что эта языковая конструкция применяется к обоим типам и значениям.

+2

Да, да, да и да. Он в основном расширяется до 'get <0> (t), get <1> (t), get <2> (t), ..., get (t)' – Xeo

+1

*** БОЛЬШОЕ ПРЕДУПРЕЖДЕНИЕ ***: никогда не используйте 'std: : forward' или 'std :: move' в расширении пакета аргументов - значение (здесь кортеж' t') разрешается только перемещать ** один раз **. Он может работать здесь, потому что 'std :: get ' на самом деле не перемещается, но это само по себе является ** анти-шаблоном **, который должен быть в состоянии определить, а затем исправить! –

+5

@ Daniel: 'std :: forward' здесь отлично, так как он извлекает только элементы кортежа и, таким образом, только перемещает их. Если вы знаете, что делаете, я не вижу проблемы, и я не думаю, что это анти-шаблон, как вы это делаете. – Xeo

ответ

6

Я думаю, что комментарий @ Xeo подвел итог. От 14.5.3 стандарта C++ 11:

Расширительный пакет состоит из шаблона и многоточия, тем конкретизации, которая производит ноль или более инстанциации в узоре в виде списка ,

В вашем случае, к тому времени, как вы закончите с рекурсивной шаблона конкретизации и в конечном итоге в частичной специализации, у вас есть

f(std::get<N>(std::forward<Tuple>(t))...); 

... где N является параметром пакет из четырех int с (0, 1, 2, и 3). Из приведенного выше standardese, то картина здесь

std::get<N>(std::forward<Tuple>(t)) 

Применение ... многоточия к вышеуказанному шаблону заставляет его быть разложен на четыре конкретизации в виде списка, т.е.

f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t)); 
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<...>, а не перемещать элементы. I думаю что-то вроде подходящего элементарного перемещения std::tuple<...> можно сделать, но я еще этого не сделал.

+1

Есть ли специальная причина для '0' вместо' nullptr' в 'make_indices'? (И причина для 'const *' вместо по-значения, используя 'return {};'?) – dyp

+0

@DyP: 'nullptr' vs.' 0': я просто не изменил использование 'nullptr'. Список индексов также можно использовать напрямую, но я не думаю, что это имеет значение. –

+0

Я не вижу, как он может перемещать весь кортеж. В стандарте говорится, что он перемещает только указанный элемент на аргумент rvalue на 'std :: get', no? – Xeo

Смежные вопросы