2009-10-16 4 views
24

Wie kann ich variable Anzahl von Parametern in meiner Funktion in C++ haben.Variable Anzahl von Parametern in Funktion in C++

Analog in 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); 
} 

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

Wie andere haben darauf hingewiesen, eine variadische Funktion ist speziell, was Sie suchen. Aber wenn Sie nicht beabsichtigen, eine Mischung von Typen an die Funktion zu senden, ist es besser, wenn Sie stattdessen einen Zeiger oder eine Referenz an einen Vektor übergeben, der Ihre Parameter enthält. –

Antwort

35

Diese Variadic functions genannt werden. Wikipedia-Listen example code for C++.

portably Um variadische Funktionen in der Programmiersprache C Sprache, die Standard stdarg.h Header Datei verwendet werden implementieren sollte. Der ältere varargs.h-Header wurde für zugunsten von stdarg.h veraltet. In C++ sollte die Header-Datei cstdarg verwendet werden.

Um eine Variadische Funktion, ein Auslassungszeichen (...) erstellen muss am Ende einer Parameterliste gesetzt. Innerhalb des Hauptteils der Funktion muss eine Variable Typ va_list definiert werden. Dann können die Makros va_start(va_list, last fixed param), va_arg(va_list, cast type), va_end(va_list) verwendet werden. Für Beispiel:

#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

Ich denke, das Beispiel ist C .. aber nahe genug. –

+0

C kompiliert normalerweise gut in C++ - Compiler. –

+0

Ich nehme an, es kompiliert, aber die Verwendung von Header anstelle von kennzeichnet es als deutlich "C". –

3

Siehe Variadic functions in C, Objective-C, C++, and D

Sie müssen stdarg.h aufzunehmen und dann va_list, va_start, va_arg und va_end, wie das Beispiel in dem Wikipedia-Artikel zeigt verwenden. Es ist ein bisschen mühsamer als in Java oder C#, weil C und C++ nur begrenzte integrierte Unterstützung für Varargs haben.

5

Abgesehen von den anderen Antworten, wenn Sie nur eine Reihe von ganzen Zahlen passieren versucht, warum nicht:

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

Sie können es nicht in Parameter aufrufen, Form, though. Sie müssten eine der Variadic-Funktionen verwenden, die in Ihren Antworten aufgeführt sind. C++ 0x erlaubt variadische Templates, die es typsicher machen, aber im Moment ist es hauptsächlich Speicher und Casting.

Sie irgendeine Art von variadische parameter- emulieren könnte> Vektor Sache:

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

Verbrauch:

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

Wenn Sie nicht über Portabilität egal, könnten Sie Port this C99 code zu C++ mit 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)); 
} 

Sie könnten etwas ähnliches mit C++ 0x tun 'Lambda-Ausdrücke, aber die gcc-Version, die ich benutze (4.4.0), unterstützt sie nicht.

1

GManNickG und Christoph Antworten sind gut, aber variadic Funktionen ermöglichen es Ihnen, in den ... Parameter was Sie wollen, nicht nur ganze Zahlen. Wenn Sie in Zukunft wollen, um viele Variablen und Werte von verschiedenen Typen in eine Funktion zu schieben, ohne variadic Funktion zu verwenden, weil es zu schwierig oder zu kompliziert für Sie ist, oder Sie mögen den Weg nicht Um es zu verwenden oder die erforderlichen Header nicht zu verwenden, können Sie immer void** Parameter verwenden.

Zum Beispiel Stephan202 posted:

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

Jetzt ist es wie auf diese Weise verwenden:

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

für vollständigen Code:

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

dies kann auch geschrieben werden

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

OUTPUT:

ist Durchschnitt: 2

Drücken Sie eine beliebige Taste, um fortzufahren. . .

9

Die echte C++ Lösung ist Variadic Templates. Sie benötigen einen relativ neuen Compiler und aktivieren bei Bedarf die C++ 11-Unterstützung.

Zwei Möglichkeiten, um das Problem zu lösen "das gleiche mit allen Funktionsargumenten zu tun": rekursiv und mit einer hässlichen (aber sehr sehr Standards-konform) Lösung.

Die recursive solution sieht etwas wie folgt aus:

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 

es für jede Sammlung von Argumenten ein Symbol erzeugt, und dann eine für jeden Schritt in die Rekursion. Dies ist suboptimal, gelinde gesagt, so the awesome C++ people here at SO Gedanken an 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)... }; 
} 

-Code ist nicht für eine Million etwas andere Vorlage instantiations erzeugt, und als Bonus, erhalten Sie erhalten, um von Ihnen Argumente funktionieren. Siehe die andere Antwort für die wesentlichen Details dieser Lösung.

3

In C++ 11 und höher können Sie auch Initialisierungslisten verwenden.

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

Sehr interessant – Hydro

Verwandte Themen