#templates #c 11 #variadic
#шаблоны #c 11 #переменный
Вопрос:
Допустим, у меня есть пакет параметров, который я разворачиваю, например
template<typename... P> void f(P...amp;amp; args) {
some_other_func(std::forward<P>(args)...);
}
Теперь давайте предположим, что у меня есть какая-то другая второстепенная функция, которую эти объекты должны выполнить.
template<typename T> Tamp;amp; some_func(Tamp;amp; ref) {
// replace with actual logic
return std::forward<T>(ref);
}
Обычно я бы просто заменил на
template<typename... P> void f(P...amp;amp; args) {
some_other_func(some_func(args)...);
}
Но что мне делать, если some_func
требуется больше информации о параметре, чем просто его тип, например, его числовое положение в пакете параметров? Так что вместо расширения до
some_other_func(some_func(arg1), some_func(arg2));
Я мог бы расширить его до
some_other_func(some_func(arg1, 1), some_func(arg2, 2));
например?
Ответ №1:
Я знаю, что решал это раньше, но не могу вспомнить как. Ну что ж, вот свежий взгляд.
Последовательность чисел может быть преобразована в последовательность аргументов с помощью std::get
, поэтому она более фундаментальна. Итак, предполагая, что мне нужно реализовать какой-то пользовательский инструмент, генератор пакетов чисел кажется хорошим выбором.
(Блин, это было невероятно утомительно. Я просмотрел ответ Говарда и узнал о forward_as_tuple
, но эта функция еще даже не существует в моем компиляторе или ideone.com ну и бла. Есть много вещей, в которых мне все еще нужно разобраться, и это, безусловно, один из худших функциональных языков, когда-либо изобретенных.)
#include <tuple>
// Generic pack array (metacontainer)
template< typename T, T ... seq > struct value_sequence {
// Append a value to the array (metafunction)
template< T val > struct append
{ typedef value_sequence< T, seq..., val > type; };
};
// Generate a sequential array (metafunction)
template< size_t N >
struct index_sequence {
typedef typename index_sequence< N - 1 >::type
::template append< N - 1 >::type type;
};
template<>
struct index_sequence< 0 >
{ typedef value_sequence< size_t > type; };
// Generate indexes up to size of given tuple (metafunction)
template< typename T >
struct index_tuple {
typedef typename index_sequence< std::tuple_size< T >::value
>::type type;
};
// The magic function: passes indexes, makes all the function calls
template< typename F, typename G,
typename T, size_t ... N >
void compose_with_indexes_helper( F f, G g, T args,
value_sequence< size_t, N ... > ) {
f( g( std::get< N >( args ), N ) ... );
}
template< typename F, typename G, typename ... T >
void compose_with_indexes( F f, G g, T amp;amp; ... args ) {
typedef std::tuple< T amp;amp; ... > tuple_t;
compose_with_indexes_helper
// forwarding seems broken on ideone.com/GCC 4.5.1, work around.
// ( f, g, std::forward_as_tuple( std::forward( args ) ... ) );
( f, g, tuple_t( args ... ), typename index_tuple< tuple_t >::type() );
}
Комментарии:
1. Спасибо за это. Конечно, не собираюсь использовать частные внутренние компоненты libstdc .
2. Единственное, что я могу придумать, что является более утомительным, чем C с шаблонами кортежей и переменных, — это C без шаблонов кортежей и переменных. 😉 Я сильно подозреваю, что со временем мы (сообщество C ) фактически стандартизируем полезные утилиты, подобные тем, которые вы показали выше, или тем, чем я воспользовался в libc . И тогда, надеюсь, все будет не так утомительно.
3. @Howard:
tuple
Мне нравится, что интерфейс пакета параметров, похоже, сопротивляется инкапсуляции.
Ответ №2:
Это немного запутанно. Но вот рабочий прототип вашего кода с использованием нескольких частных утилит libc , найденных в <__tuple> и <tuple>.
#include <iostream>
#include <tuple>
template<typename T>
int
some_func(Tamp;amp; ref, size_t I)
{
std::cout << "ref = " << ref << ", I = " << I << 'n';
return 0;
}
template<typename... T, size_t ...Indx>
void
some_other_func(std::tuple<T...> ref, std::__tuple_indices<Indx...>) {
// replace with actual logic
std::__swallow(some_func(std::get<Indx>(ref), Indx)...);
}
template<typename... P>
void
f(Pamp;amp;... args)
{
some_other_func(std::forward_as_tuple<P...>(std::forward<P>(args)...),
typename std::__make_tuple_indices<sizeof...(P)>::type());
}
int main()
{
f("zero", "one", "two", "three");
}
ref = zero, I = 0
ref = one, I = 1
ref = two, I = 2
ref = three, I = 3
Комментарии:
1. Смехотворно, даже не собираюсь пытаться! 🙂