Вариативные шаблоны

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

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


person sami    schedule 03.09.2010    source источник
comment
Что вы имеете в виду под компилируемым примером? Я думаю, что все эти ссылки представили примеры, которые можно скомпилировать.   -  person kennytm    schedule 03.09.2010
comment
нет, это не так. Эти примеры описывают только замедление таких проверочных шаблонов, также может быть определением, но никогда не вызовом в случае вариативной функции или экземпляром некоторых вариадических классов в основной функции. На самом деле мне нужен простой пример, который я могу скопировать и скомпилировать, чтобы лучше понять вариативный механизм.   -  person sami    schedule 03.09.2010
comment
Вы уверены, что используете компилятор, который его поддерживает, и вызываете его с правильными параметрами?   -  person sellibitze    schedule 03.09.2010
comment
Собственно, я просто имел в виду, что мне нужны несколько примеров для его компиляции. Я знаю, что должен быть установлен флаг компиляции -std = c ++ 0x. Однако все примеры, которые я нашел, похожи на printf. Мне нужен небольшой, но содержательный пример ..   -  person sami    schedule 03.09.2010


Ответы (8)


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

person David Feurle    schedule 03.09.2010
comment
Верно. Насколько мне известно, компилятор Microsoft пока не поддерживает вариативные шаблоны. Просто говорю. - person sellibitze; 03.09.2010
comment
Они объявили о некоторых функциях, которые появятся в MSVC11 (в 2012 году), а шаблонов Variadic Templates не было в этом списке. Пора мне научиться устанавливать MinGW. - person Mooing Duck; 24.11.2011
comment
@ Virus721 он жаловался, что нет компилируемого примера. Существует множество примеров - единственная причина, по которой они не компилируются: отсутствие поддержки компилятора или отсутствие переключателя компилятора. - person David Feurle; 28.11.2013

Одним из простейших возможных примеров является следующая реализация 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";
}
person Motti    schedule 05.09.2010
comment
Является ли ... после Args и args просто синтаксической солью, или есть также способы использования вариативных параметров без ...? - person Stewart; 18.10.2011
comment
Размещение ... имеет значение. Рассмотрим две вариативные функции шаблона sum и product. Теперь вызовите их из другого вариативного шаблона с параметрами 2,3,4, значение product(sum(args...)) будет 9, а значение product(sum(args)...) будет 24. - person Motti; 27.10.2011
comment
@Stewart Я немного расширил эту тему в блоге сообщение - person Motti; 07.11.2011
comment
означает ли строка printf(s, args...); рекурсивный вызов во время выполнения другой функции, скомпилированной с одним аргументом меньше, или компилятор рекурсивно расширяет эту строку до тела printf с одним аргументом меньше? - person Youda008; 14.12.2014
comment
@ youda008 это технически не рекурсия, компилятор создает новую перегрузку printf с одним параметром меньше и добавляет вызов этой функции. Этот вызов функции может быть или не быть встроенным, но это не имеет значения. - person Motti; 14.12.2014

Вариативные шаблоны - это функция 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:

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

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

ваше здоровье! s

person sellibitze    schedule 03.09.2010

person phadej    schedule 03.09.2010
comment
да, я смотрел на это. Я попытался создать пример, чтобы скомпилировать его, но безуспешно. Мне нужен компилируемый пример, который, как я уже сказал, демонстрирует этот подход, запустив его. - person sami; 03.09.2010

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

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

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

so on...

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

person Saurav Sahu    schedule 28.12.2017
comment
Спасибо за подробный и простой ответ. Не могу поверить, что я нашел что-то подобное в конце ответов. - person NetherGranite; 26.08.2018

Это пример вариативных шаблонов, которые я разместил в своем блоге: 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;
};
person Jarryd    schedule 23.11.2011
comment
Ммммм, я всегда думал, что C ++ был создан для более удобочитаемого синтаксиса, чем C ... - person Virus721; 27.11.2013
comment
Может быть, в некоторых случаях. Но вы не можете сделать это даже на C, поэтому это несопоставимо. - person Jarryd; 28.11.2013

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

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

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

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

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

person Sergey    schedule 19.05.2015

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

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> >
person Erez    schedule 14.06.2017