Переменное число параметров в функции в C++

Как я могу иметь переменное число параметров в функции в C++.

аналог в C#:

public void Foo(params int[] a) {
    for (int i = 0; i < a.Length; i++)
        Console.WriteLine(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(1, 2);
}

аналог в Java:

public void Foo(int... a) {
    for (int i = 0; i < a.length; i++)
        System.out.println(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(2);
}

8 ответов


это называется variadic функции. Википедия списки пример кода для C++.

для переносимой реализации variadic функции программирования C язык, стандартный включаемом файле stdarg.h файл должен использоваться. Старейшина с varargs.заголовок h устарел в пользу stdarg.h. в C++ заголовочный файл cstdarg должен быть использован.

создать вариативную функцию, является многоточие (...) необходимо поместить на конец списка параметров. Внутри тело функции, переменная тип va_list должна быть определена. Затем макросы va_start(va_list, last fixed param), va_arg(va_list, cast type), va_end(va_list) можно использовать. Для пример:

#include <stdarg.h>

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}

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

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

на рекурсивное решение выглядит так:

template<typename... ArgTypes>
void print(ArgTypes... args);
template<typename T, typename... ArgTypes>
void print(T t, ArgTypes... args)
{
  std::cout << t;
  print(args...);
}
template<> void print() {} // end recursion

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

struct expand_type {
  template<typename... T>
  expand_type(T&&...) {}
};
template<typename... ArgTypes>
void print(ArgTypes... args)
{ 
  expand_type{ 0, (std::cout << args, 0)... };
}

код не генерируется для миллиона немного разных экземпляров шаблона,и в качестве бонуса вы получаете сохраненный порядок аргументов функции. См. другой ответ для деталей nitty gritty этого решения.


помимо других ответов, если вы просто пытаюсь передать массив целых чисел, почему бы и нет:

void func(const std::vector<int>& p)
{
    // ...
}

std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);

func(params);

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

вы можете эмулировать какой-то вариационный параметр - >векторная вещь:

// would also want to allow specifying the allocator, for completeness
template <typename T> 
std::vector<T> gen_vec(void)
{
    std::vector<T> result(0);
    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1)
{
    std::vector<T> result(1);

    result.push_back(a1);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2)
{
    std::vector<T> result(1);

    result.push_back(a1);
    result.push_back(a2);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2, T a3)
{
    std::vector<T> result(1);

    result.push_back(a1);
    result.push_back(a2);
    result.push_back(a3);

    return result;
}

// and so on, boost stops at nine by default for their variadic templates

использование:

func(gen_vec(1,2,3));

посмотреть функции с переменным количеством аргументов в C++, цель-C, С++ и D

вы должны включить включаемом файле stdarg.h и затем использовать va_list, va_start, va_arg и va_end, как показывает пример в статье Википедии. Это немного более громоздко, чем в Java или C#, потому что C и C++ имеют только ограниченную встроенную поддержку varargs.


в C++11 и более поздних версиях вы также можете использовать списки инициализаторов.

int sum(const initializer_list<int> &il)
{
    int nSum = 0;
    for (auto x: il) 
        nSum += x;
    return nsum;
}

cout << sum( { 3, 4, 6, 9 } );

Если вы не заботитесь о переносимости, вы можете port этот код C99 на C++ с помощью выражения заявления gcc:

#include <cstdio>

int _sum(size_t count, int values[])
{
    int s = 0;
    while(count--) s += values[count];
    return s;
}

#define sum(...) ({ \
    int _sum_args[] = { __VA_ARGS__ }; \
    _sum(sizeof _sum_args / sizeof *_sum_args, _sum_args); \
})

int main(void)
{
    std::printf("%i", sum(1, 2, 3));
}

вы можете сделать что-то подобное с лямбда-выражениями C++0x, но версия gcc, которую я использую (4.4.0), не поддерживает их.


GManNickG и Кристоф ответы хороши, но вариативные функции позволяют нажимать в ... параметр все, что вы хотите, а не только целые числа. Если ты ... --6-->будет хотите в будущее, чтобы нажать многие переменные и значения разные вводит в функцию без использования функции variadic, потому что это слишком сложно или слишком сложно для вас, или вам не нравится способ ее использования, или вы не хотите включать необходимые заголовки для использования его, то вы всегда можете использовать void**


Я делаю так в C++ builder xe.xx:

String s[] = {"hello ", " unli", " param", " test"};
String ret = BuildList(s, 4);

String BuildList(String s[], int count)
{
    for(int i = 0; i < count; i++)
    {
        //.... loop here up to last s[i] item ... 
    }
}