Wie kann ich eine Funktion schreiben, die eine variable Anzahl von Argumenten akzeptiert? Ist das möglich, wie?
Variable Anzahl von Argumenten in C++?
nunos
Shafik Yaghmur
Im C++11 Sie haben zwei neue Optionen, als die Variadische Argumente Bezugsseite im Abschnitt Alternativen Zustände:
- Variadische Vorlagen können auch verwendet werden, um Funktionen zu erstellen, die eine variable Anzahl von Argumenten annehmen. Sie sind oft die bessere Wahl, da sie keine Beschränkungen für die Typen der Argumente auferlegen, keine Ganzzahl- und Gleitkomma-Umwandlungen durchführen und typsicher sind. (seit C++11)
- Wenn alle Variablenargumente einen gemeinsamen Typ haben, bietet eine std::initializer_list einen bequemen Mechanismus (wenn auch mit einer anderen Syntax) für den Zugriff auf Variablenargumente.
Unten ist ein Beispiel, das beide Alternativen zeigt (live sehen):
#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 } ) ;
}
Wenn Sie verwenden gcc
oder clang
wir können die verwenden SCHÖNE_FUNKTION magische Variable um die Typsignatur der Funktion anzuzeigen, die hilfreich sein kann, um zu verstehen, was vor sich geht. Zum Beispiel mit:
std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;
würde im Beispiel für variadische Funktionen folgendes int ergeben (live sehen):
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
In Visual Studio können Sie verwenden FUNCSIG.
Update vor C++11
Vor C++11 die Alternative für std::initializer_list wäre std::Vektor oder einer der anderen Standardbehälter:
#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 ) ;
}
und die Alternative für abwechslungsreiche Vorlagen wäre variadische Funktionen obwohl sie es nicht sind typsicher und allgemein fehleranfällig und kann unsicher in der Anwendung sein aber die einzige andere mögliche Alternative wäre die Verwendung Standardargumente, obwohl das nur begrenzten Nutzen hat. Das folgende Beispiel ist eine modifizierte Version des Beispielcodes in der verknüpften Referenz:
#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 ;
}
Verwenden variadische Funktionen kommt auch mit Einschränkungen in den Argumenten, die Sie übergeben können, die in detailliert beschrieben werden Entwurf des C++-Standards im Abschnitt 5.2.2
Funktionsaufruf Absatz 7:
Wenn es für ein gegebenes Argument keinen Parameter gibt, wird das Argument so übergeben, dass die empfangende Funktion den Wert des Arguments erhalten kann, indem sie va_arg (18.7) aufruft. Die Lvalue-zu-rvalue- (4.1), Array-zu-Zeiger- (4.2) und Funktion-zu-Zeiger- (4.3) Standardkonvertierungen werden für den Argumentausdruck durchgeführt. Wenn das Argument nach diesen Konvertierungen keinen Arithmetik-, Enumerations-, Zeiger-, Zeiger-auf-Member- oder Klassentyp hat, ist das Programm falsch formatiert. Wenn das Argument einen Nicht-POD-Klassentyp hat (Klausel 9), ist das Verhalten undefiniert. […]
-
Ist dein
typename
vsclass
Verwendung über beabsichtigt? Wenn ja, bitte erläutern.– Kevinarpe
30. September 2016 um 10:08 Uhr
-
@kevinarpe nicht beabsichtigt, es ändert jedoch nichts.
– Shafik Yaghmour
30. September 2016 um 12:34 Uhr
-
Ihr erster Link sollte wahrscheinlich zu wahrscheinlich zu sein en.cppreference.com/w/cpp/language/variadic_arguments stattdessen.
– Alexej Romanow
6. April 2018 um 12:00 Uhr
-
ist es möglich, eine Funktion zu machen, die a nimmt
initializer_list
rekursiv?– 463035818_ist_keine_Nummer
26. Oktober 2018 um 22:30 Uhr
wilhelmell
Das sollten Sie wahrscheinlich nicht, und Sie können wahrscheinlich auf sicherere und einfachere Weise tun, was Sie tun möchten. Technisch gesehen, um eine variable Anzahl von Argumenten in C zu verwenden, schließen Sie stdarg.h ein. Daraus erhälst du die va_list
Typ sowie drei Funktionen, die darauf operieren aufgerufen va_start()
, va_arg()
und va_end()
.
#include<stdarg.h>
int maxof(int n_args, ...)
{
va_list ap;
va_start(ap, n_args);
int max = va_arg(ap, int);
for(int i = 2; i <= n_args; i++) {
int a = va_arg(ap, int);
if(a > max) max = a;
}
va_end(ap);
return max;
}
Wenn Sie mich fragen, ist das ein Durcheinander. Es sieht schlecht aus, ist unsicher und voller technischer Details, die nichts mit dem zu tun haben, was Sie konzeptionell erreichen möchten. Erwägen Sie stattdessen die Verwendung von Überladung oder Vererbung/Polymorphismus, Builder-Muster (wie in operator<<()
in Streams) oder Standardargumente usw. Diese sind alle sicherer: Der Compiler erfährt mehr darüber, was Sie zu tun versuchen, sodass es mehr Gelegenheiten gibt, in denen er Sie aufhalten kann, bevor Sie sich das Bein brechen.
-
Vermutlich können Sie keine Referenzen an eine varargs-Funktion übergeben, da der Compiler nicht wissen würde, wann er als Wert und wann als Referenz übergeben werden soll, und weil die zugrunde liegenden C-Makros nicht unbedingt wissen würden, was mit Referenzen zu tun ist – es gibt bereits Einschränkungen für was Sie können aufgrund von Dingen wie Heraufstufungsregeln in eine C-Funktion mit variablen Argumenten übergehen.
– Jonathan Leffler
1. November 2009 um 19:24 Uhr
-
ist es notwendig, mindestens ein Argument vor dem anzugeben
...
Syntax?– Laser
23. Juni 2010 um 9:54 Uhr
-
@Lazer Es ist keine Sprach- oder Bibliotheksanforderung, aber die Standardbibliothek gibt Ihnen keine Möglichkeit, die Länge der Liste anzugeben. Sie brauchen den Anrufer, um Ihnen diese Informationen zu geben oder es irgendwie selbst herauszufinden. Im Falle des
printf()
analysiert die Funktion beispielsweise das Zeichenfolgenargument nach speziellen Tokens, um herauszufinden, wie viele zusätzliche Argumente sie in der Variablenargumentliste erwarten sollte.– wilhelmtel
23. Juni 2010 um 21:33 Uhr
-
sollten Sie wahrscheinlich verwenden
<cstdarg>
in C++ statt<stdarg.h>
– neues Konto
8. Januar 2013 um 21:05 Uhr
-
Eine variable Anzahl von Argumenten eignet sich hervorragend zum Debuggen oder für Funktionen/Methoden, die ein Array füllen. Es eignet sich auch hervorragend für viele mathematische Operationen wie Max, Min, Summe, Durchschnitt … Es ist kein Chaos, wenn Sie sich nicht damit beschäftigen.
– Tomáš Zato – Wiedereinsetzung von Monica
5. Dezember 2014 um 20:49 Uhr
YSC
Eine C++17-Lösung: vollständige Typsicherheit + schöne Aufrufsyntax
Seit der Einführung von Variadic-Templates in C++11 und Fold-Ausdrücken in C++17 ist es möglich, eine Template-Funktion zu definieren, die auf der Aufruferseite aufrufbar ist, als wäre sie eine Varidic-Funktion, aber mit den Vorteilen :
- stark typsicher sein;
- arbeiten ohne die Laufzeitinformationen der Anzahl der Argumente oder ohne die Verwendung eines “stop”-Arguments.
Hier ist ein Beispiel für gemischte Argumenttypen
template<class... Args>
void print(Args... args)
{
(std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");
Und ein weiterer mit erzwungener Typübereinstimmung für alle Argumente:
#include <type_traits> // enable_if, conjuction
template<class Head, class... Tail>
using are_same = std::conjunction<std::is_same<Head, Tail>...>;
template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
void print_same_type(Head head, Tail... tail)
{
std::cout << head;
(std::cout << ... << tail) << "\n";
}
print_same_type("2: ", "Hello, ", "World!"); // OK
print_same_type(3, ": ", "Hello, ", "World!"); // no matching function for call to 'print_same_type(int, const char [3], const char [8], const char [7])'
// print_same_type(3, ": ", "Hello, ", "World!");
^
Mehr Informationen:
- Variadische Vorlagen, auch als Parameterpaket bekannt Parameterpaket (seit C++11) – cppreference.com.
- Ausdrücke falten Faltungsausdruck (seit C++17) – cppreference.com.
- Ein … sehen vollständige Programmdemonstration auf coliru.
-
Eines Tages hoffe ich, dass ich lesen kann
template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
– Eladian
19. April 2018 um 7:11 Uhr
-
@Eladian las es als “Dieses Ding ist nur aktiviert, wenn
Head
undTail...
sind gleich“, wo “sind gleich” bedeutetstd::conjunction<std::is_same<Head, Tail>...>
. Lesen Sie diese letzte Definition als “Head
ist das gleiche wie alleTail...
“.– JSC
20. April 2018 um 16:09 Uhr
-
Können wir eine Schleife machen?
args
?– AbbasEbadian
30. Juni 2021 um 12:25 Uhr
in c++11 können Sie Folgendes tun:
void foo(const std::list<std::string> & myArguments) {
//do whatever you want, with all the convenience of lists
}
foo({"arg1","arg2"});
Listeninitialisierer FTW!
Allmächtig
In C++11 gibt es eine Möglichkeit, Vorlagen mit variablen Argumenten zu erstellen, die zu einer wirklich eleganten und typsicheren Möglichkeit führen, Funktionen mit variablen Argumenten zu haben. Bjarne selbst gibt ein schönes Beispiel dafür printf mit variablen Argumentvorlagen in dem C++11FAQ.
Ich persönlich halte das für so elegant, dass ich mich nicht einmal mit einer Funktion für variable Argumente in C++ beschäftigen würde, bis dieser Compiler Unterstützung für C++11-Vorlagen für variable Argumente bietet.
-
@donlan – Wenn Sie C ++ 17 verwenden, können Sie Faltausdrücke verwenden, um die Dinge in einigen Fällen viel einfacher zu machen (denken Sie hier kreativ, Sie können die
,
Operator mit Faltausdrücken). Ansonsten glaube ich nicht.– Allmächtig
12. Januar 2020 um 17:30 Uhr
-
Netter Artikel, auf den verwiesen wird.
– Razzia
18. Januar 2021 um 20:30 Uhr
Variadic-Funktionen im C-Stil werden in C++ unterstützt.
Die meisten C++-Bibliotheken verwenden jedoch ein alternatives Idiom, z. B. während die 'c' printf
Die Funktion übernimmt variable Argumente c++ cout
Objekt nutzt <<
Überladen, das Typsicherheit und ADTs adressiert (vielleicht auf Kosten der Einfachheit der Implementierung).
-
@donlan – Wenn Sie C ++ 17 verwenden, können Sie Faltausdrücke verwenden, um die Dinge in einigen Fällen viel einfacher zu machen (denken Sie hier kreativ, Sie können die
,
Operator mit Faltausdrücken). Ansonsten glaube ich nicht.– Allmächtig
12. Januar 2020 um 17:30 Uhr
-
Netter Artikel, auf den verwiesen wird.
– Razzia
18. Januar 2021 um 20:30 Uhr
Francesco
Abgesehen von Varargs oder Überladen könnten Sie in Betracht ziehen, Ihre Argumente in einem std::vector oder anderen Containern (z. B. std::map) zu aggregieren. Etwas wie das:
template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);
Auf diese Weise würden Sie Typsicherheit gewinnen und die logische Bedeutung dieser vielfältigen Argumente wäre offensichtlich.
Sicherlich kann dieser Ansatz Leistungsprobleme haben, aber Sie sollten sich darüber keine Gedanken machen, es sei denn, Sie sind sicher, dass Sie den Preis nicht bezahlen können. Es ist eine Art “Pythonic”-Ansatz für C++ …
-
Sauberer wäre es, Vektoren nicht zu erzwingen. Verwenden Sie stattdessen ein Vorlagenargument, das die Sammlung im STL-Stil angibt, und durchlaufen Sie sie dann mit den Methoden begin und end des Arguments. Auf diese Weise können Sie std::vector
, std::array von c++11, std::initializer_list verwenden oder sogar Ihre eigene Sammlung erstellen. – Jens Akerblom
29. März 2013 um 11:26 Uhr
-
@JensÅkerblom Ich stimme zu, aber dies ist die Art von Wahl, die für das vorliegende Problem analysiert werden sollte, um eine Überkonstruktion zu vermeiden. Da dies eine Frage der API-Signatur ist, ist es wichtig, den Kompromiss zwischen maximaler Flexibilität und Klarheit der Absicht/Benutzerfreundlichkeit/Wartbarkeit usw. zu verstehen.
– Francesco
30. März 2013 um 14:18 Uhr
Zu dieser Zeit mit C++11 Die Antworten auf diese Frage würden sehr unterschiedlich sein
– K-ballo
8. Januar 2013 um 20:07 Uhr
@K-ballo Ich habe auch C ++ 11-Beispiele hinzugefügt, da kürzlich eine Frage dasselbe gestellt hat, und ich hatte das Gefühl, dass dies eines braucht, um das Schließen zu rechtfertigen. stackoverflow.com/questions/16337459/…
– Shafik Yaghmour
3. Mai 2013 um 13:46 Uhr
Vorher hinzugefügt C++11 Optionen zu meiner Antwort, daher sollte es jetzt die meisten verfügbaren Optionen abdecken.
– Shafik Yaghmour
2. Dezember 2013 um 18:32 Uhr
@K-ballo Es gibt afaik keine Möglichkeit, dies in C ++ zu tun, falls Sie einen erzwungenen Argumenttyp benötigen. Keine Konstruktion wie foo (int … values): / Wenn Sie sich nicht für Typen interessieren, dann ja, variadische Vorlagen in C++11 funktioniert super
– grauer Wolf
7. August 2016 um 21:03 Uhr
stackoverflow.com/a/29326784/4361073
– Parasisch
24. November 2017 um 13:08 Uhr