Variadic шаблоны

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

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

8 ответов

Решение

Шаблоны Variadic являются частью стандарта C++0x, который еще официально не выпущен. Они поддерживаются gcc начиная с версии 4.3, но вам нужно включить поддержку C++0x, добавив ключ компилятора -std= C++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 - это функция 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 пока не предлагает этот шаблон функции. Вы должны написать его самостоятельно)
  • шаблоны с переменным числом (принимающих произвольное количество параметров)

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

ура! s

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

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

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

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

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

скоро...

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

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

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

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

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

Поскольку в C++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> >
Другие вопросы по тегам