Ответ 1
Шаблоны Variadic являются частью стандарта С++ 0x, который еще официально не выпущен. Они поддерживаются gcc с версии 4.3, но вам нужно включить поддержку С++ 0x, добавив компилятор -std = С++ 0x.
Я видел много ссылок, в которых представлены вариативные шаблоны. Но я никогда не видел компилятивного примера, демонстрирующего этот подход.
Может ли кто-нибудь предоставить мне некоторые ссылки, в которых можно найти такие компилируемые примеры?
Шаблоны Variadic являются частью стандарта С++ 0x, который еще официально не выпущен. Они поддерживаются gcc с версии 4.3, но вам нужно включить поддержку С++ 0x, добавив компилятор -std = С++ 0x.
Одним из простейших возможных примеров является следующая реализация max
, которая даже не шаблонна для типов.
int maximum(int n)
{
return n;
}
template<typename... Args>
int maximum(int n, Args... args)
{
return max(n, maximum(args...));
}
Только немного сложнее каноническая реализация printf
:
void printf(const char *s)
{
while (*s)
{
if (*s == '%' && *(++s) != '%')
throw "invalid format string: missing arguments";
std::cout << *s++;
}
}
template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
while (*s)
{
if (*s == '%' && *(++s) != '%')
{
std::cout << value;
printf(s, args...); // call even when *s == 0 to detect extra arguments
return;
}
std::cout << *s++;
}
throw "extra arguments provided to printf";
}
Шаблоны Variadic - это функция С++ 0x, которая в первую очередь нацелена на авторов общих библиотек. Я бы не ожидал увидеть их в "коде пользователя". Например, в стандартной библиотеке С++ 0x они используются во многих местах: std:: function, std:: async, std:: reference_wrapper, std:: tuple, std:: packaged_task,...
Чтобы дать вам пример, я покажу вам, как может быть реализован reference_wrapper в отношении вариативных шаблонов:
template<class T>
class reference_wrapper
{
T *ptr;
public:
explicit reference_wrapper(T& thing) : ptr(&thing) {}
explicit reference_wrapper(T&& ) = delete;
operator T&() const {return *ptr;}
template<class... Args>
decltype( declval<T&>()(declval<Args>()...) )
operator()(Args&&... args) const
{
return (*ptr)(forward<Args>(args)...);
}
};
Это не совсем соответствует стандарту, но он должен быть скомпилирован с небольшими изменениями. Он демонстрирует несколько возможностей С++ 0x:
decltype
declval
для создания объектов с целью создания выражения для decltype
(GCC пока не предлагает этот шаблон функции. Вы должны сами написать его)Цель шаблона Variadic-члена - перенаправить аргументы на объект, на который ссылается ptr
. Это должно работать в случае, если T - тип указателя функции или тип класса с перегруженным оператором вызова функции.
ура! з
Это пример вариационных шаблонов, которые я выставил в своем блоге: http://thenewcpp.wordpress.com/2011/11/23/variadic-templates-part-1-2/
Он компилируется. Он демонстрирует поиск самого большого типа из группы типов.
#include <type_traits>
template <typename... Args>
struct find_biggest;
//the biggest of one thing is that one thing
template <typename First>
struct find_biggest<First>
{
typedef First type;
};
//the biggest of everything in Args and First
template <typename First, typename... Args>
struct find_biggest<First, Args...>
{
typedef typename find_biggest<Args...>::type next;
typedef typename std::conditional
<
sizeof(First) >= sizeof(next),
First,
next
>::type type;
};
Перед С++ 11 вы можете создать шаблон только с фиксированным количеством параметров.
Создает шаблон для функции с одним параметром.
Второй шаблон для функции с двумя параметрами. ... т.е.
Так как С++ 11 вы можете написать только один шаблон, компилятор будет генерировать нужную функцию.
Хороший пример http://eli.thegreenplace.net/2014/variadic-templates-in-c/
другой синтаксис: расширение, например
template<typename VAL, typename... KEYS>
class MyMaps
{
typedef std::tuple< std::map<KEYS,VAL>... > Maps;
}
отсюда:
MyMaps<int,int,string>:Maps
теперь фактически:
std::tuple<std::map<int,int>,std::map<string,int> >
Очень простой пример вариационного шаблона:
Предположим, мы хотим иметь функцию, которая принимает переменное количество аргументов и печатает их все. Для примера:
print("Hello", 1, 3.14, 5L);
Для этой функциональности нам потребуется в основном две функции:
Первая, функция, которая принимает переменное количество аргументов:
template<typename T, typename... Args>
void print(T t, Args ...args){
std::cout << t << ", ";
print(args...);
}
Некоторое объяснение:
1.) Пакеты параметров, обозначенные ellipsis (...), которые отображаются в списке параметров.
typename...Args
| | << Optional whitespace. Can have multiple whitespaces in between them
Args...args
Это означает, что все они одинаковы.
typename ...args
typename...args
typename ... args
Итак, вам не нужно беспокоиться о правильной позиции пробелов. Хотя ИМО не более чем в одном пробеле следует использовать как наилучшую практику.
2.) Расширение пакета: шаблон, за которым следует многоточие.
print(args...); //expand when you wish to use them
3.) Пакет параметров принимает ноль или более аргументов шаблона. Таким образом, print(T t, Args... args)
принимает один или несколько аргументов.
Как только вы это поймете, мы можем визуализировать поток вызовов, как показано ниже:
print("Hello", 1, 3.14, 5L);
означает:
print(string, int, float, long);
который вызывает
print(int, float, long);
который вызывает
print(float, long); // say Level 2
который вызывает
print(long); // say Level 1
который вызывает
print(); // say Level 0
Если вы внимательно следили за пунктом №3, вы должны были понять, что print(T t, Args... args)
не может обрабатывать вызов на уровне 0.
Поэтому нам нужна другая функция с таким же именем, чтобы догнать любой уровень >= 0.
Вторая, функция для захвата вызова в верхней части стека вызовов:
Поймать уровень 0:
void print(){}
или, Поймать на уровне 1:
template<typename T>
void print(T t){ std::cout << t;}
или, Поймать на уровне 2:
template<typename T, typename U>
void print(T t, U u){ std::cout << t << ", " << u;}
и так далее...
Любое из них будет работать. Надеюсь, это поможет в следующий раз, когда вы начнете писать такую функцию или класс.