Подтвердить что ты не робот

Расширение с помощью вариативных шаблонов

В чем разница между следующими 3 вызовами функции gun?

template <class... Ts> void fun(Ts... vs) {
  gun(A<Ts...>::hun(vs)...);
  gun(A<Ts...>::hun(vs...));
  gun(A<Ts>::hun(vs)...);
}

Мне интересен ответ, объясняющий три вызова с использованием конкретного примера.

4b9b3361

Ответ 1

Изначально я буквально ответил на этот вопрос, но я хотел немного расширить его, чтобы дать более подробное объяснение того, как пакеты расширяются. Так или иначе я думаю о вещах.

Любая упаковка, сразу же за которой следуют эллипсы, просто расширяется. Итак, A<Ts...> эквивалентно A<T1, T2, ..., TN>, а hun(vs...) аналогично эквивалентно hun(v1, v2, ..., vn). Там, где это осложняется, вместо того, чтобы пакет, за которым следуют эллипсы, вы получаете что-то вроде ((expr)...). Это расширится до (expr1, expr2, ..., exprN), где expri относится к исходному выражению с любым пакетом, замененным его версией i th. Поэтому, если у вас есть hun((vs+1)...), это становится hun(v1+1, v2+1, ..., vn+1). Там, где становится все веселее, expr может содержать более одного пакета (если все они имеют одинаковый размер!). Вот как мы реализуем стандартную идеальную модель пересылки;

foo(std::forward<Args>(args)...)

Здесь expr содержит две упаковки (Args и Args - оба пакета), а расширение "итерации" по обоим:

foo(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), ..., std::forward<ArgN>(argN));

Это рассуждение должно позволить вам быстро пройтись по вашим делам, скажем, что происходит, когда вы вызываете foo(1, 2, '3').

Первая, gun(A<Ts...>::hun(vs)...); расширяет Ts "на месте", а затем появляется выражение для расширения для последних эллипсов, поэтому это вызывает:

gun(A<int, int, char>::hun(1), 
    A<int, int, char>::hun(2), 
    A<int, int, char>::hun('3'));

Второй, gun(A<Ts...>::hun(vs...)); расширяет обе пакеты на месте:

gun(A<int, int, char>::hun(1, 2, '3'));

Третий, gun(A<Ts>::hun(vs)...), одновременно расширяет обе упаковки:

gun(A<int>::hun(1), 
    A<int>::hun(2), 
    A<char>::hun('3'));

[обновление] Для полноты gun(A<Ts>::hun(vs...)...) вызовет:

gun(A<int>::hun(1, 2, '3'),
    A<int>::hun(1, 2, '3'),
    A<char>::hun(1, 2, '3'));

Наконец, есть один последний случай, чтобы рассмотреть, где мы выходим за борт на эллипсах:

gun(A<Ts...>::hun(vs...)...);

Это не будет компилироваться. Мы расширяем как Ts, так и vs "на месте", но тогда у нас нет пакетов для расширения для конечных эллипсов.

Ответ 2

Здесь, как они расширяются, когда Ts есть T, U и vs есть t, u:

gun(A<Ts...>::hun(vs)...) -> gun(A<T, U>::hun(t), A<T, U>::hun(u))
gun(A<Ts...>::hun(vs...)) -> gun(A<T, U>::hun(t, u));
gun(A<Ts>::hun(vs)...)    -> gun(A<T>::hun(t), A<U>::hun(u))

И еще один случай, который вы не рассматривали:

gun(A<Ts>::hun(vs...)...) -> gun(A<T>::hun(t, u), A<U>::hun(t, u))

Если вы запустите код ниже в VS14, вы получите этот результат:

calling gun(A<Ts...>::hun(vs)...);
    struct A<int,double>::hun(double);
    struct A<int,double>::hun(int);
    gun(struct A<int,double>, struct A<int,double>);
calling gun(A<Ts...>::hun(vs...));
    struct A<int,double>::hun(int, double);
    gun(struct A<int,double>);
calling gun(A<Ts>::hun(vs)...);
    struct A<double>::hun(double);
    struct A<int>::hun(int);
    gun(struct A<int>, struct A<double>);
calling gun(A<Ts>::hun(vs...)...);
    struct A<double>::hun(int, double);
    struct A<int>::hun(int, double);
    gun(struct A<int>, struct A<double>);

код:

#include <iostream>
#include <typeinfo>

using namespace std;

void printTypes() {}

template<typename T, typename... Ts> void printTypes(T, Ts... vs) {
    cout << typeid(T).name() << (sizeof...(Ts) ? ", " : "");
    printTypes(vs...);
}

template<typename... Ts> struct A {
    template<typename... Us>
    static auto hun(Us... vs) {
        cout << "    " << typeid(A).name() << "::hun(";
        printTypes(vs...);
        cout << ");" << endl;
        return A{}; 
    }
};

template<typename... Ts> void gun(Ts... vs) {
    cout << "    gun(";
    printTypes(vs...);
    cout << ");" << endl;
}

template<typename... Ts> void fun(Ts... vs) {
  cout << "calling gun(A<Ts...>::hun(vs)...);" << endl;
  gun(A<Ts...>::hun(vs)...);
  cout << "calling gun(A<Ts...>::hun(vs...));" << endl;
  gun(A<Ts...>::hun(vs...));
  cout << "calling gun(A<Ts>::hun(vs)...);" << endl;
  gun(A<Ts>::hun(vs)...);
  cout << "calling gun(A<Ts>::hun(vs...)...);" << endl;
  gun(A<Ts>::hun(vs...)...);
}

int main() {
    fun(1, 2.0);
}