2009-10-16 19 views
24

Jak mogę mieć zmienną liczbę parametrów w mojej funkcji w C++.Zmienna liczba parametrów w funkcji w C++

analogowe w 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); 
} 

analogowe w Javie:

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

Jak inni podkreślili, funkcja variadic jest dokładnie tym, czego szukasz. Ale jeśli nie masz zamiaru wysyłać kombinacji typów do funkcji, lepiej jest po prostu przekazać wskaźnik lub odwołanie do wektora zawierającego parametry. –

Odpowiedz

35

Są to tak zwane Variadic functions. Wikipedia wymienia example code for C++.

przenośną wdrożyć zmiennej liczbie argumentów funkcji w języku programowania C , średnia stdarg.h nagłówek plik powinien być stosowany. Starszy nagłówek varargs.h został wycofany na korzyść stdarg.h. W języku C++ należy użyć pliku nagłówkowego cstdarg.

celu utworzenia zmiennej liczbie argumentów funkcji elipsa (...) musi być umieszczona w końcu listy parametrów. Wewnątrz obiektu funkcji musi być zdefiniowana zmienna typu va_list. Następnie można zastosować makra , które można zastosować. Dla przykład:

#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; 
} 
+1

Myślę, że ten przykład to C. .. ale wystarczająco blisko. –

+0

C zwykle kompiluje się dobrze w kompilatorach C++. –

+0

Zakładam, że kompiluje, ale użycie nagłówka zamiast oznacza to wyraźnie "C". –

5

Oprócz innych odpowiedzi, jeśli jesteś po prostu staramy się przekazać tablicę liczb całkowitych, to dlaczego nie:

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

nie można nazwać go w parametrze, forma, choć. Musiałbyś użyć dowolnej z funkcji variadycznych wymienionych w twoich odpowiedziach. C++ 0x zezwoli na szablony variadyczne, które spowodują, że będzie to bezpieczny typ, ale na razie jest to w zasadzie pamięć i przesyłanie.

Można emulować jakieś zmiennej liczbie argumentów parameter-> wektor rzecz:

// 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 

Zastosowanie:

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

Jeśli nie dbają o przenośności, można portu this C99 code do C++ przy użyciu gcc's statement expressions :

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

Można zrobić coś podobnego przy pomocy C++ 0x "wyrażenia lambda, ale wersja gcc, której używam (4.4.0), nie obsługuje ich.

1

Odpowiedzi GManNickG i Christoph są dobre, ale funkcje variadyczne pozwalają ci wprowadzać parametry, które chcesz, nie tylko liczby całkowite. Jeśli chcesz uzyskać w przyszłym, wypchnąć wiele zmiennych i wartości różnych typów do funkcji bez użycia funkcji variadic, ponieważ jest to zbyt trudne lub zbyt skomplikowane dla Ciebie, lub nie podoba Ci się sposób Aby go użyć lub nie chcesz dołączać wymaganych nagłówków, aby go użyć, zawsze możesz użyć parametru: void** .

Na przykład Stephan202 pisał:

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

ten można również zapisać jako:

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

teraz używać go jak w ten sposób:

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

do pełnego kodu:

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

WYJŚCIE:

Średnia ocena: 2

Naciśnij dowolny klawisz, aby kontynuować. . .

9

Prawdziwym rozwiązaniem C++ są szablony variadyczne. Będziesz potrzebował dość aktualnego kompilatora i w razie potrzeby wesprzyj obsługę C++ 11.

Dwa sposoby radzenia sobie z problemem "rób tak samo ze wszystkimi argumentami funkcji": rekurencyjnie i z brzydkim (ale bardzo zgodnym ze standardami) rozwiązaniem.

recursive solution wygląda nieco jak poniżej:

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 

Generuje jeden symbol dla każdego zbioru argumentów, a następnie po jednym dla każdego kroku do rekursji. To nie jest optymalne do co najmniej, więc the awesome C++ people here at SO myśl a great trick abusing the side effect of a list initialization:

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

Kodeks nie jest generowany za milion nieznacznie różnych dawałaby szablonu, a jako bonus, zostaniesz zachowany porządek z was funkcjonować argumenty. Zobacz drugą odpowiedź na szczegółowe szczegóły tego rozwiązania.

3

W C++ 11 i nowszych można również użyć list inicjalizujących.

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

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

Bardzo interesujące – Hydro