Шаблоны Variadic - различные типы расширений

Андрей Александреску дал отличный разговор под названием: Шаблоны Variadic являются funadic.

Он представляет следующие 3 разложения, которые являются подрезанными разными:

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

Он объясняет:

Вызов 1: Расширяет все Ts для обеспечения устойчивости class A, Затем вызывает hun(vs) Затем снова расширяется все параметры при передаче их в gun

Вызов 2: Расширяет все Ts и все vs отдельно

Вызов 3: Expnads в шаге блокировки, то есть: Разверните аргумент 1 из Ts и аргумент 1 vs Разверните аргумент 2 из Ts и аргумент 2 vs Разверните аргумент n из Ts и аргумент n vs

Другое обсуждение вариационных шаблонов, по-видимому, охватывает простые шаблоны вариационного класса и вариативные функции, такие как typafe printf и т.д. Я не уверен, как эти различные типы расширения влияют на код и где каждый тип будет полезен.

Есть ли у кого-нибудь примеры, демонстрирующие применение каждого типа расширения?

Ответ 1

#include <iostream>
#include <memory>
#include <typeinfo>
#include <cstdlib>
#include <cxxabi.h>

template <typename T>
std::unique_ptr<char, void(*)(void*)>
type_name()
{
    return std::unique_ptr<char, void(*)(void*)>
           (
                __cxxabiv1::__cxa_demangle(typeid(T).name(), nullptr,
                                           nullptr, nullptr),
                std::free
           );
}

void display() {}

template <class T>
void
display()
{
    std::cout << type_name<T>().get() << ' ';
}

template <class T, class T2, class ...Tail>
void
display()
{
    std::cout << type_name<T>().get() << ' ';
    display<T2, Tail...>();
}

template <class... Ts>
struct A
{
    template <class... Us>
        static
        int
        hun(Us... us)
        {
            std::cout << "A<";
            display<Ts...>();
            std::cout << ">::hun(";
            display<Us...>();
            std::cout << ")\n";
            return 0;
        }
};

template <class ...T>
void gun(T...) {}

template <class... Ts> void fun( Ts... vs )
{
    std::cout << "gun( A<Ts...>::hun(vs)...);\n";
    gun( A<Ts...>::hun(vs)...);
    std::cout << "\ngun( A<Ts...>::hun(vs...));\n";
    gun( A<Ts...>::hun(vs...));
    std::cout << "\ngun( A<Ts>::hun(vs)...);\n";
    gun( A<Ts>::hun(vs)...);
}

int main()
{
    fun(1, 'a', 2.3);
}

Вывод:

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

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

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

Ответ 2

Случаи 2 и 3 действительно очень распространены в любом виде кода, включающем вариативные пакеты.

template<typename... T>
void f(T&&... t)
{
    // Case 2:
    auto t2 = std::tuple<T...>(t...);

    // Case 3:
    auto t3 = std::make_tuple(std::forward<T>(t)...);
}

Посмотрев на мой собственный код, я не могу найти какой-либо выживший пример случая 1. Возможно, я использовал его в прошлом в некотором пространстве имен detail для вспомогательного tempate, но я не уверен. Я не думаю, что это будет распространено или даже необходимо большую часть времени.