Variadic шаблоны - PullRequest
       23

Variadic шаблоны

20 голосов
/ 03 сентября 2010

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

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

Ответы [ 8 ]

23 голосов
/ 05 сентября 2010

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

Шаблоны Variadic - это функция C ++ 0x, которая в основном предназначена для авторов универсальных библиотек.Я не ожидал бы увидеть их в «код пользователя».Например, в стандартной библиотеке C ++ 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)...);
    }
};

Это не совсем соответствует стандартному черновику, но предполагается, чтокомпилируется с небольшими изменениями.Он демонстрирует несколько функций C ++ 0x:

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

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

ура!s

5 голосов
/ 28 декабря 2017

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

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

print("Hello", 1, 3.14, 5L);

Для того, чтобы эта функциональность работала, нам в основном потребовалось бы две функции:

Первая, функция, которая принимает переменное число аргументов:

template<typename T, typename... Args>
void print(T t, Args ...args){
     std::cout << t << ", ";
     print(args...);
}

Некоторое объяснение:

1.) Пакеты параметров, обозначенные многоточием (...), которые появляются в списке параметров.

typename...Args 
        |  | << Optional whitespace. Can have multiple whitespaces in between them
    Args...args

Это означает, что этивсе одинаковы.

typename ...args
typename...args
typename   ...   args

Так что вам не нужно беспокоиться о правильном расположении пропусков там.Тем не менее, в качестве наилучшей практики следует использовать IMO не более одного пробела.

2.) Расширение пакета: шаблон с последующим многоточием.

print(args...); //expand when you wish to use them

3.) Пакет параметров принимает ноль или более шаблонов args.Итак, 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;}

и т. Д ...

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

5 голосов
4 голосов
/ 24 ноября 2011

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

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

0 голосов
/ 14 июня 2017

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

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> >
0 голосов
/ 19 мая 2015

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...