#include <cstdio>
#include <utility>
using namespace std;
struct Heavy
{
Heavy() { printf("h "); }
Heavy(const Heavy&) { printf("H "); }
};
template<int N, class... Args> struct getter;
template<class A, class... Args> struct getter<0,A,Args...>
{
typedef A type;
static type get(A a, Args&&...) { printf("0 "); return a; }
};
template<int N, class A, class... Args> struct getter<N,A,Args...>
{
typedef getter<N-1,Args...> base;
typedef typename base::type type;
static type get(A a, Args&&... args)
{
printf("%d ", N);
return base::get(forward<Args>(args)...);
}
};
template<int N, class... Args>
typename getter<N,Args...>::type
get(Args&&... args)
{
printf("go ");
return getter<N,Args...>::get(forward<Args>(args)...);
}
int main()
{
get<4>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
get<5>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
get<9>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
}
Получаем:
h go 4 3 2 1 0
h go 5 4 3 2 1 H 0 H
h go 9 8 7 6 5 H 4 3 2 1 0
Вместо прежнего
К>вывод: К>
К>h go H 4 H 3 H 2 H 1 H 0
К>h go H 5 H 4 H 3 H 2 H 1 H 0 H
К>h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0
К>
К>Вот какое бешеное количество копирований.
Копирования остаются из-за этого:
typedef A type;
static type get(A a, Args&&...) { printf("0 "); return a; }
static type get(A a, Args&&... args)
typename getter<N,Args...>::type
get(Args&&... args)
Тут принимаем по значению и возвращаем.
Если заменить значения на ссылки, то получим 0 копий !
type -> type const&
Здравствуйте, Аноним, Вы писали:
А>Или есть более интересные способы?
Существенно более интересный способ — это реализовать вариадики вручную, с помощью expression templates.
Очень грубо говоря,
// на вариадиках
foo(1, 2, 3, 4, 5);
// на префиксных ET
foo(cons(1, cons(2, cons(3, cons(4, cons(5, nil)))))); // правый список
foo(snoc(snoc(snoс(snoс(snoc(lin, 1), 2), 3), 4), 5)); // левый список
// на инфиксных ET
foo(start() << 1 << 2 << 3 << 4 << 5); // snoc-выражение, где в роли lin - start(), snoc - operator<<
Особенность ET в том, что
— аргументы можно хранить и доставать по ссылке
— список может быть перебалансирован в дерево
— доступны всякие плюшки вроде обхода списка каким-либо визитёром
template<class... Args> struct arglist; // на самом деле, можно даже без вариадиков обойтись - это я просто не вдаюсь в реализацию arglist
//...MAGIC...MAGIC...MAGIC...struct F
{
template<class A> void operator()(A const& a) {.....}
};
struct G
{
template<class A> void operator()(int i, A const& a) {.....}
};
template<class... Args>
void foo(arglist<Args...> const& args)
{
int constexpr n = args.size();
for_each_arg(args, F());
for_each_indexed_arg(args, G());
}
int main()
{
foo(start());
foo(start() << 1 << "hello" << 'x');
}
Перекуём баги на фичи!
Re[7]: Как вытащить конкретный аргумент из variadic template
_>С применением функции std::forward полностью согласен, но с возвратом и передачей параметров по ссылке не всё так однозначно, как кажется на первый взгляд. Предлагаю взглянуть на статью Dave Abrahams <b>Want Speed? Pass by Value.</b>
Я в курсе про эту статью.
Нужно немного поиграться с кодом, чтобы добиться отсутствия копий, с ссылками это было проще всего
Здравствуйте, _smit, Вы писали:
RO>>Здравствуйте, _smit, Вы писали: _>>><b>Want Speed? Pass by Value.</b> RO>>А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.
_>Откуда тогда у _NN_ в логах копирование? Судя по тому, как представлено определение get<N>(args...), там, вроде, не аргумент подставляются, а из рекурсии конечные функции подставляются, которые возвращают нужный аргумент. Т.е. рекурсия в compiled-time, а в RT производится возврат значения из рассчитанной функции. Поэтому я и отметил, что обозначение типа возвращаемого значения в виде ссылки может поставить крест на RVO (return value optimization). (но я бы тоже ссылки поставил... :) )
Причем тут RVO? Здесь же что-нибудь вроде
template<class... Args>
void do_something(Args... args) // или &, или &&, это уже другой вопрос
{
. . .
do_something_else(get<1>(args...));
. . .
}
do_something(a, b, 42);
и воплотиться это должно в
void do_something(A a, B b, int c)
{
. . .
do_something_else(b); // perfect forwarding же
. . .
}
Функция get<> инлайнится полностью, нет и речи о том, чтобы копировать что-либо у нее на входе или на выходе.
До последнего не верил в пирамиду Лебедева.
Как вытащить конкретный аргумент из variadic template
От:
Аноним
Дата:
27.08.13 13:35
Оценка:
Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого
Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.
Re[3]: Как вытащить конкретный аргумент из variadic template
Здравствуйте, FrozenHeart, Вы писали:
FH>Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.
Здравствуйте, FrozenHeart, Вы писали:
FH>Так ведь это то же самое, что и я написал, разве нет? Синтаксис тот же самый в итоге. Например, требуется в compile-time знать индекс аргумента, к которому обращаешься.
С той разницей, что
— нет нужды писать get_first, get_second, ..., get_nineth, ... или делать соответствующие специализации get<0>, get<1>, ..., get<9>, ... — достаточно единственной специализации getter<0> и арифметики
— нет нужды переупаковывать россыпь аргументов в кортеж, с тем, чтобы потом извлечь один
Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование, то может случиться беда N раз, тогда как со специализациями она не случится, а с кортежом — случится единожды.
Перекуём баги на фичи!
Re[4]: Как вытащить конкретный аргумент из variadic template
Здравствуйте, Кодт, Вы писали:
К>Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование,
А зачем копировать , perfect-forwarding на что ?
Здравствуйте, Аноним, Вы писали:
А>Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого
А>
Здравствуйте, _smit, Вы писали:
_>Здравствуйте, Аноним, Вы писали: А>>Я правильно понимаю, что единственный способ обратиться к конкретному аргументу из variadic template (учитывая, что все они могут быть разных типов) — это сделать что-то наподобие такого
...
_>Да, правильно! тип возвращаемого значения через класс свойств, прмер в: tuple_element _>Второй, приведенный тобой способ мне меньше нравится, т.к. "ручная работа". А>>Или есть более интересные способы?
Так,.. почитав внимательнее ответы от Кодт, понял, что речь о функциональном программировании, а я опять о классах [facepalm]... мой предыдущий ответ... создавать кортеж для доступа к аргументам функции -- тяжеловесно...
Смотрим код от Кодт...
Re[5]: Как вытащить конкретный аргумент из variadic template
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Кодт, Вы писали:
К>>Разумеется, рекурсивная функция плоха тем, что если у N-го аргумента нетривиальное и тяжеловесное копирование, _NN>А зачем копировать , perfect-forwarding на что ?
#include <cstdio>
struct Heavy
{
Heavy() { printf("h "); }
Heavy(const Heavy&) { printf("H "); }
};
template<int N, class... Args> struct getter;
template<class A, class... Args> struct getter<0,A,Args...>
{
typedef A type;
static type get(A a, Args...) { printf("0 "); return a; }
};
template<int N, class A, class... Args> struct getter<N,A,Args...>
{
typedef getter<N-1,Args...> base;
typedef typename base::type type;
static type get(A a, Args... args) { printf("%d ", N); return base::get(args...); }
};
template<int N, class... Args>
typename getter<N,Args...>::type
get(Args... args) { printf("go "); return getter<N,Args...>::get(args...); }
int main()
{
get<4>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
get<5>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
get<9>(0,1,2,3,4, Heavy(), 6,7,8,9);
printf("\n");
}
вывод:
h go H 4 H 3 H 2 H 1 H 0
h go H 5 H 4 H 3 H 2 H 1 H 0 H
h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0
Вот какое бешеное количество копирований.
Что сюда нужно прикрутить, чтобы perfect-forwarding заработало из коробки? Обернуть в cref(Heavy()) не получается, там заблокировано создание reference_wrapper из rvalue reference.
Своё писать, рукодельное?
Перекуём баги на фичи!
Re[7]: Как вытащить конкретный аргумент из variadic template
Здравствуйте, _NN_, Вы писали: _NN>Здравствуйте, Кодт, Вы писали: К>>Это как? _NN>Для начала добавляем && и std::forward: _NN>http://ideone.com/Onx2la _NN>Вместо прежнего
... К>>вывод: К>>
К>>h go H 4 H 3 H 2 H 1 H 0
К>>h go H 5 H 4 H 3 H 2 H 1 H 0 H
К>>h go H 9 H 8 H 7 H 6 H 5 H 4 3 2 1 0
К>>
К>>Вот какое бешеное количество копирований.
... _NN>Тут принимаем по значению и возвращаем. _NN>Если заменить значения на ссылки, то получим 0 копий ! _NN>type -> type const&
_NN>http://ideone.com/QWKJgG
_NN>
_NN>h go 4 3 2 1 0
_NN>h go 5 4 3 2 1 0
_NN>h go 9 8 7 6 5 4 3 2 1 0
_NN>
С применением функции std::forward полностью согласен, но с возвратом и передачей параметров по ссылке не всё так однозначно, как кажется на первый взгляд. Предлагаю взглянуть на статью Dave Abrahams <b>Want Speed? Pass by Value.</b>
Re[8]: Как вытащить конкретный аргумент из variadic template
А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.
До последнего не верил в пирамиду Лебедева.
Re[9]: Как вытащить конкретный аргумент из variadic template
Здравствуйте, Roman Odaisky, Вы писали: RO>Здравствуйте, _smit, Вы писали: _>><b>Want Speed? Pass by Value.</b> RO>А причем тут скорость, когда речь о подстановках в compile time? Ведь get<N>(args...) просто раскроется в выбор одного из аргументов с нулевым оверхедом в RT.
Откуда тогда у _NN_ в логах копирование? Судя по тому, как представлено определение get<N>(args...), там, вроде, не аргумент подставляются, а из рекурсии конечные функции подставляются, которые возвращают нужный аргумент. Т.е. рекурсия в compiled-time, а в RT производится возврат значения из рассчитанной функции. Поэтому я и отметил, что обозначение типа возвращаемого значения в виде ссылки может поставить крест на RVO (return value optimization). (но я бы тоже ссылки поставил... )
Re[8]: Как вытащить конкретный аргумент из variadic template