Шаблоны Variadic

Я видел много ссылок, в которых представлены вариативные шаблоны. Но я никогда не видел компилятивного примера, демонстрирующего этот подход.

Может ли кто-нибудь предоставить мне некоторые ссылки, в которых можно найти такие компилируемые примеры?

Ответы

Ответ 1

Шаблоны Variadic являются частью стандарта С++ 0x, который еще официально не выпущен. Они поддерживаются gcc с версии 4.3, но вам нужно включить поддержку С++ 0x, добавив компилятор -std = С++ 0x.

Ответ 2

Одним из простейших возможных примеров является следующая реализация 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";
}

Ответ 3

Шаблоны 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:

  • удаленные функции (отключение конструктора для значений r)
  • ссылки rvalue (обнаружение аргументов rvalue для конструктора, безупречная переадресация)
  • вывод типа через decltype
  • шаблон стандартной библиотеки declval для создания объектов с целью создания выражения для decltype (GCC пока не предлагает этот шаблон функции. Вы должны сами написать его)
  • variadic templates (принимает произвольное количество параметров)

Цель шаблона Variadic-члена - перенаправить аргументы на объект, на который ссылается ptr. Это должно работать в случае, если T - тип указателя функции или тип класса с перегруженным оператором вызова функции.

ура! з

Ответ 5

Это пример вариационных шаблонов, которые я выставил в своем блоге: 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;
};

Ответ 6

Перед С++ 11 вы можете создать шаблон только с фиксированным количеством параметров.

Создает шаблон для функции с одним параметром.

Второй шаблон для функции с двумя параметрами. ... т.е.

Так как С++ 11 вы можете написать только один шаблон, компилятор будет генерировать нужную функцию.

Хороший пример http://eli.thegreenplace.net/2014/variadic-templates-in-c/

Ответ 7

другой синтаксис: расширение, например

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> >

Ответ 8

Очень простой пример вариационного шаблона:

Предположим, мы хотим иметь функцию, которая принимает переменное количество аргументов и печатает их все. Для примера:

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;}

и так далее...

Любое из них будет работать. Надеюсь, это поможет в следующий раз, когда вы начнете писать такую ​​функцию или класс.