В C ++ 11 у вас есть две новые опции, как справочная страница Variadic в разделе Альтернативы состояния:
- Шаблоны Variadic также можно использовать для создания функций, которые принимают переменное число
аргументы. Они часто являются лучшим выбором, потому что они не накладывают ограничений на
типы аргументов, не выполнять целочисленные и с плавающей запятой, и
Тип безопасности. (начиная с C ++ 11)
- Если все переменные аргументы имеют общий тип, std :: initializer_list предоставляет
удобный механизм (хотя и с другим синтаксисом) для доступа к переменным аргументам.
Ниже приведен пример, показывающий обе альтернативы ( посмотреть вживую ):
#include <iostream>
#include <string>
#include <initializer_list>
template <typename T>
void func(T t)
{
std::cout << t << std::endl ;
}
template<typename T, typename... Args>
void func(T t, Args... args) // recursive variadic function
{
std::cout << t <<std::endl ;
func(args...) ;
}
template <class T>
void func2( std::initializer_list<T> list )
{
for( auto elem : list )
{
std::cout << elem << std::endl ;
}
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
func(1,2.5,'a',str1);
func2( {10, 20, 30, 40 }) ;
func2( {str1, str2 } ) ;
}
Если вы используете gcc
или clang
, мы можем использовать PRETTY_FUNCTION магическую переменную для отображения сигнатуры типа функции, которая может быть полезно в понимании того, что происходит. Например, используя:
std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;
будет приводить к следующему int для переменных функций в примере ( посмотреть вживую ):
void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1
void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5
void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a
void func(T) [T = std::basic_string<char>]: Hello
В Visual Studio вы можете использовать FUNCSIG .
Обновление до C ++ 11
Pre C ++ 11 альтернативой для std :: initializer_list будет std :: vector или один из других стандартных контейнеров * * 1060
#include <iostream>
#include <string>
#include <vector>
template <class T>
void func1( std::vector<T> vec )
{
for( typename std::vector<T>::iterator iter = vec.begin(); iter != vec.end(); ++iter )
{
std::cout << *iter << std::endl ;
}
}
int main()
{
int arr1[] = {10, 20, 30, 40} ;
std::string arr2[] = { "hello", "world" } ;
std::vector<int> v1( arr1, arr1+4 ) ;
std::vector<std::string> v2( arr2, arr2+2 ) ;
func1( v1 ) ;
func1( v2 ) ;
}
и альтернатива для шаблонов переменных будет функций , хотя они не являются типобезопасными и в целом подвержены ошибкам и могут быть небезопасными использовать , но единственной возможной альтернативой будет использование аргументов по умолчанию , хотя это ограниченное применение. Пример ниже представляет собой модифицированную версию примера кода в связанной ссылке:
#include <iostream>
#include <string>
#include <cstdarg>
void simple_printf(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
std::cout << i << '\n';
} else if (*fmt == 's') {
char * s = va_arg(args, char*);
std::cout << s << '\n';
}
++fmt;
}
va_end(args);
}
int main()
{
std::string
str1( "Hello" ),
str2( "world" );
simple_printf("dddd", 10, 20, 30, 40 );
simple_printf("ss", str1.c_str(), str2.c_str() );
return 0 ;
}
Использование переменных функций также имеет ограничения в аргументах, которые вы можете передать, что подробно описано в черновом стандарте C ++ в разделе 5.2.2
Вызов функции параграф 7
Когда для данного аргумента нет параметра, аргумент передается таким образом, что принимающая функция может получить значение аргумента, вызвав va_arg (18.7). Стандартные преобразования lvalue-to-rvalue (4.1), массив-указатель (4.2) и функция-указатель (4.3) выполняются в выражении аргумента. После этих преобразований, если аргумент не имеет арифметику, перечисление, указатель, указатель на член или тип класса, программа становится некорректной. Если аргумент имеет тип класса не POD (раздел 9), поведение не определено. [...]