Подтвердить что ты не робот

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

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

Аналог в С#:

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);
}
4b9b3361

Ответ 1

Они называются Функции Variadic. Википедия перечисляет пример кода для С++.

Чтобы обеспечить переносимость переменных функции в программировании на С язык, стандартный stdarg.h header файл должен использоваться. Старший Заголовок varargs.h устарел в пользу stdarg.h. В С++ заголовочный файл 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;
}

Ответ 2

Реальное решение С++ - это вариативные шаблоны. Вам понадобится довольно недавний компилятор и, если нужно, включить поддержку С++ 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

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

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

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

Ответ 3

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

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

Однако вы не можете называть его в параметре, форме. Вам нужно будет использовать любую вариационную функцию, указанную в ваших ответах. С++ 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));

Ответ 4

См. Функции Variadic в C, Objective-C, С++ и D

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

Ответ 5

В С++ 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 } );

Ответ 6

Если вы не заботитесь о переносимости, вы можете передать этот код C99 на С++, используя выражения выражения 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));
}

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

Ответ 7

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

Например, опубликован Stephan202:

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

это также можно записать как:

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
       tot+=*(double*)params[j];
    return tot/count;
}

Теперь используйте его следующим образом:

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
}

для полного кода:

#include "stdafx"
#include <process.h>

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
        tot+=*(double*)params[j];
    return tot/count;
}

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
 }

ВЫВОД:

Среднее значение: 2

Нажмите любую клавишу, чтобы продолжить.,.