Variable Anzahl von Argumenten in C++?

Lesezeit: 11 Minuten

Variable Anzahl von Argumenten in C
nunos

Wie kann ich eine Funktion schreiben, die eine variable Anzahl von Argumenten akzeptiert? Ist das möglich, wie?

  • 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

Variable Anzahl von Argumenten in C
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 vs class 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

1647213012 465 Variable Anzahl von Argumenten in C
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

Variable Anzahl von Argumenten in C
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:

  1. Variadische Vorlagen, auch als Parameterpaket bekannt Parameterpaket (seit C++11) – cppreference.com.
  2. Ausdrücke falten Faltungsausdruck (seit C++17) – cppreference.com.
  3. 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 und Tail... sind gleich“, wo “sind gleich” bedeutet std::conjunction<std::is_same<Head, Tail>...>. Lesen Sie diese letzte Definition als “Head ist das gleiche wie alle Tail...“.

    – 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!

1647213012 302 Variable Anzahl von Argumenten in C
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


1647213013 718 Variable Anzahl von Argumenten in C
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

999300cookie-checkVariable Anzahl von Argumenten in C++?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy