Поделиться через


Многоточия и шаблоны с переменными аргументами

В этой статье показано, как использовать многоточие (...) в шаблонах C++ с переменным числом аргументов. Многоточие активно использовалось в C и C++. Они вводят переменные списки аргументов для функций. Одним из наиболее известных примеров является функция printf() из библиотеки времени выполнения C .

Шаблон с переменным числом аргументов — это шаблон класса или функции, поддерживающий произвольное число аргументов. Этот механизм особенно удобен для разработчиков библиотек C++, поскольку его можно применить к как к шаблонам классов, так и к шаблонам функций. Таким образом, он предоставляет широкий спектр широкий спектр типобезопасных и нетривиальных функций и гибких возможностей.

Синтаксис

В шаблонах шаблонами с переменным числом аргументов многоточие используется двумя способами. Слева от имени параметра оно означает пакет параметров, а справа от имени параметра оно служит для развертывания пакетов параметров в отдельные имена.

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

template<typename... Arguments> class classname;

В обоих случаях (как при введении пакета параметров, так и при его развертывании) вокруг многоточия можно оставить пробельные символы, как показано в этом примере:

template<typename ...Arguments> class classname;

Или в этом:

template<typename ... Arguments> class classname;

Обратите внимание, что в этой статье используется соглашение, показанное в первом примере (многоточие примыкает к имени типа — typename).

В приведенных выше примерах параметр Arguments означает пакет параметров. Класс classname может принимать переменное число аргументов, как показано в следующих примерах:

template<typename... Arguments> class vtclass;

vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;

Кроме того, определение шаблонного класса с переменным числом аргументов может устанавливать требование о том, что должен быть передан по меньшей мере один параметр:

template <typename First, typename... Rest> class classname; 

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

template <typename... Arguments> returntype functionname(Arguments... args);

Далее пакет параметров Arguments затем развертывается для использования, как показано в следующем разделе, Основные сведения о шаблонах с переменным числом аргументов.

Возможны и другие формы синтаксиса шаблонной функции с переменным количеством аргументов возможны. Некоторые примеры приведены ниже.

template <typename... Arguments> returntype functionname(Arguments&... args); 
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);

Также допускаются спецификаторы, например const.

template <typename... Arguments> returntype functionname(const Arguments&... args); 

Шаблонные функции с переменным числом аргументов (как и аналогичные шаблонные классы) также могут устанавливать требование о том, что должен быть передан по меньшей мере один параметр.

template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args); 

В шаблонах с переменным числом аргументов используется оператор sizeof...() (он не имеет отношения к старому оператору sizeof()).

template<typename... Arguments>
void tfunc(const Arguments&... args)
{
    const unsigned numargs = sizeof...(Arguments);

    X xobj[numargs]; // array of some previously defined type X

    helper_func(xobj, args...);
}

Дополнительные сведения о положении многоточия

Выше в этой статье говорилось, что если многоточие определяет пакеты параметров и их развертывание, то "Слева от имени параметра оно означает пакет параметров, а справа от имени параметра оно служит для развертывания пакетов параметров в отдельные имена". Технически это верно, но может порождать неоднозначности при трансляции в код. Рассмотрим этот пример:

  • В списке-параметров-шаблона (template <parameter-list>) параметр typename... вводит в программу пакет параметров шаблона.

  • В предложении объявления параметра (func(parameter-list)) многоточие "верхнего уровня" вводит пакет параметров функции, и положение многоточия имеет большое значение.

    // v1 is NOT a function parameter pack:
    template <typename... Types> void func1(std::vector<Types...> v1); 
    
    // v2 IS a function parameter pack:
    template <typename... Types> void func2(std::vector<Types>... v2); 
    
  • Там, где многоточие стоит непосредственно за именем параметра, оно используется для развертывания пакета параметров.

Пример

Механизм действия шаблонных функций с переменным числом аргументов можно проиллюстрировать на показательном примере — переписать с ее использованием какую-либо функциональность printf:

#include <iostream>

using namespace std;

void print() {
    cout << endl;
}

template <typename T> void print(const T& t) {
    cout << t << endl;
}

template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
    cout << first << ", ";
    print(rest...); // recursive call using pack expansion syntax
}

int main()
{
    print(); // calls first overload, outputting only a newline
    print(1); // calls second overload
    
    // these call the third overload, the variadic template, 
    // which uses recursion as needed.
    print(10, 20);
    print(100, 200, 300);
    print("first", 2, "third", 3.14159);
}

Выходные данные

1
10, 20
100, 200, 300
first, 2, third, 3.14159

Примечание

Большинство реализаций, которые включают шаблонные функции с переменным числом аргументов, используют рекурсии в том или ином виде, однако она отличается от стандартной рекурсии. Стандартная рекурсия включает функцию, которая вызывает сама себя с использованием той же сигнатуры. (Это может быть перегрузка или шаблон, но каждый раз выбирается одна и та же сигнатура.) Рекурсия с переменным числом аргументов заключается в вызове шаблона функции с переменным числом аргументов посредством использования другого (почти всегда уменьшающегося) числа аргументов. Таким образом, каждый раз отбрасывается новая сигнатура."Базовый случай" по-прежнему является обязательным, но природа рекурсии отличается.

См. также

Ссылки

Многоточие (...)