Шаблоны Variadic — различные типы расширения
-
11-12-2019 - |
Вопрос
Андрей Александреску выступил с прекрасным докладом на тему: Вариадические шаблоны являются Фунадическими.
Он представляет следующие три расширения, которые существенно отличаются:
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:Расширяется синхронно, т.е.:Развернуть аргумент 1 из Ts
и аргумент 1 из vs
Развернуть аргумент 2 из Ts
и аргумент 2 из vs
Развернуть аргумент n из Ts
и аргумент n из vs
Другое обсуждение шаблонов с переменным числом вариантов, похоже, охватывает только простые шаблоны с переменным числом классов и функции с переменным числом вариантов, такие как типобезопасный printf и т. д.Я не уверен, как эти различные типы расширения влияют на код и где каждый тип будет полезен.
Есть ли у кого-нибудь примеры, демонстрирующие применение каждого типа расширения?
Решение
#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 и 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
пространство имен для вспомогательного шаблона, но я не уверен.Я не думаю, что в большинстве случаев это будет обычным явлением или даже необходимым.