¿Cómo puedo escribir una función que acepta un número variable de argumentos? Es esto posible, ¿cómo?

  • En este momento con C++11 las respuestas para esta pregunta, sería de gran difieren
  • He añadido C++11 ejemplos, así como una reciente pregunta lo mismo hace poco y sentí que necesitaba para justificar el cierre stackoverflow.com/questions/16337459/…
  • Añadido pre C++11 opciones a mi respuesta, así que ahora debe cubrir la mayoría de las opciones disponibles.
  • hay afaik no hay manera de hacerlo en C++ en el caso de que necesite forzado tipo de argumento.. no de la construcción como foo(int valores…) : / Si no te importa acerca de los tipos, entonces sí, variadic plantillas en C++11 grandes obras
  • stackoverflow.com/a/29326784/4361073
  • Por favor, considere la posibilidad de cambio de la marca de verificación para Shafik realidad-C++ respuesta. Esta es una muy alta votación de la pregunta, y la correcta-desde-C++11 respuesta debería ser que la gente ve en la parte superior, por todas las razones wilhelmtell dio en su pre-C++11 de respuesta.

InformationsquelleAutor nunos | 2009-11-01

16 Comentarios

  1. 143

    Usted probablemente no debería, y usted probablemente puede hacer lo que quiere hacer en un más seguro y simple. Técnicamente el uso de número variable de argumentos en C se incluyen stdarg.h. Desde que usted va a obtener la va_list tipo así como de las tres funciones que operan sobre ella llama va_start(), va_arg() y 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;
    }

    Si me preguntan a mí, esto es un desastre. Se ve mal, es peligroso, y está lleno de detalles técnicos que no tienen nada que ver con lo que te conceptualmente tratando de lograr. En su lugar, considere el uso de una sobrecarga o de herencia/polimorfismo, el generador de patrón (como en operator<<() en los arroyos) o argumentos predeterminados, etc. Estos son todos más seguros: el compilador se llega a conocer más acerca de lo que estamos tratando de hacer por lo que hay más ocasiones se puede detener antes de que sople su pierna.

    • Probablemente, usted no puede pasar referencias a un varargs función, ya que el compilador no sabría cuándo paso por valor y por referencia, y porque el subyacente C macros no necesariamente saben qué hacer con referencias — ya hay restricciones en lo que puede pasar a una función de C con la variable de argumentos del porque de las cosas, como las reglas de la promoción.
    • Que en realidad no importa. El problema es bastante soluble en teoría, por lo que ISO WG21 puede decir simplemente: «Esta es la especificación, ahora usted puede hacer el trabajo.». No hay ninguna buena razón por la que el compilador de C++ de decisiones tendría que limitarse a C macros. Por ejemplo, un trivial solución sería empujar RTTI información en la pila antes de cada vararg argumento. Pero como wilhemtell explica correctamente, incluso si el compilador podría, usted todavía tiene que desagradable interfaz.
    • es necesario proporcionar al menos un argumento antes de la ... sintaxis?
    • no es una lengua de la biblioteca o de requerimiento, pero la biblioteca estándar de no darle significa decir la longitud de la lista. Usted necesita la persona que llama para darles esta información o bien de alguna manera, descubrelo por ti mismo. En el caso de printf(), por ejemplo, la función analiza el argumento de cadena de fichas especiales para averiguar cómo muchos argumentos adicionales que deben esperar en la lista de argumentos variable.
    • usted probablemente tendrá que usar <cstdarg> en C++ en lugar de <stdarg.h>
    • Tiene que haber al menos un parámetro antes de ..., cuyo nombre se le pasa va_start. Esto es para permitir implementaciones donde la macro utiliza la dirección de parámetro para averiguar las direcciones de los demás (otras implementaciones podría, por ejemplo, el uso de algunos compilador integrado para acceder a la pila o el puntero de marco).
    • Yo estaba pensando en usar varargs para implementar un max(int,int,int..) de la función. Lo que sería una alternativa (y cuidado) manera de hacerlo?
    • Número Variable de argumentos es ideal para depurar o para funciones/métodos que llenar algunos matriz. También es genial para muchas operaciones matemáticas, tales como max, min, sum, medias… no Es lío cuando no meterse con él.
    • ¿Por qué no implementar un max que toma un array de int?
    • matriz no sería una buena opción para mí, como no voy a ser capaz de crear y aprobar la matriz de forma dinámica.
    • Para una función max definitivamente, usted debe utilizar un vararg plantilla.

  2. 353

    En C++11 usted tiene dos opciones nuevas, como la Variadic funciones página de referencia en el Alternativas sección estados:

    • Variadic plantillas también puede ser utilizado para crear las funciones que toman número variable de
      argumentos. Ellos son a menudo la mejor opción debido a que no se imponen restricciones sobre
      los tipos de los argumentos, no realizar integral y de punto flotante de promociones, y
      son el tipo de seguro. (debido a que C++11)
    • Si todos los argumentos variables compartir un tipo común, un std::initializer_list proporciona un
      mecanismo útil (aunque con una sintaxis diferente) para acceder a la variable de argumentos.

    A continuación es un ejemplo que muestra alternativas (ver en directo):

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

    Si usted está usando gcc o clang podemos utilizar el PRETTY_FUNCTION la magia de la variable para mostrar el tipo de la firma de la función que puede ser útil en la comprensión de lo que está pasando. Por ejemplo el uso de:

    std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;

    iba a resultados de int siguientes para variadic funciones en el ejemplo (ver en directo):

    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

    En Visual Studio puede utilizar FUNCSIG.

    Actualización de Pre C++11

    Pre C++11 la alternativa para std::initializer_list sería std::vector o uno de los otros los contenedores estándar:

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

    y la alternativa para variadic plantillas sería variadic funciones aunque no son tipo de seguro y, en general, propenso a errores y pueden ser inseguras pero la única otra alternativa sería el uso de predeterminado argumentos, a pesar de que tiene un uso limitado. El siguiente ejemplo es una versión modificada del código de la muestra en los enlaces de referencia:

    #include <iostream>
    #include <string>
    #include <cstdarg>
    
    void simple_printf(const char *fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
    
        while (*fmt != '
    #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 ;
    } 
    '
    ) { 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 ; }

    Utilizando variadic funciones también viene con restricciones en los argumentos que puede pasar que se detalla en el proyecto de estándar de C++ en la sección 5.2.2 llamada a la Función párrafo 7:

    Cuando no hay ningún parámetro para un determinado argumento, el argumento se pasa de tal manera que la función de recepción se puede obtener el valor del argumento invocando va_arg (18.7). El lvalue-a-r-value (4.1), la matriz-a-puntero (4.2), y la función de puntero (4.3) estándar de las conversiones se realizan en el argumento de la expresión. Después de estas conversiones, si el argumento no tiene aritmética, la enumeración, puntero, puntero a miembro, o el tipo de clase, el programa está mal formado. Si el argumento no POD tipo de clase (cláusula 9), el comportamiento es indefinido. […]

    • Es su typename vs class de uso por encima intencional? Si es así, por favor explique.
    • no es intencional, esto no cambia nada, aunque.
    • Su primer enlace debería de ser a ser, probablemente, en.cppreference.com/w/cpp/language/variadic_arguments en su lugar.
    • es posible hacer una función que recibe una initializer_list recursiva?
  3. 19

    en c++11 que usted puede hacer:

    void foo(const std::list<std::string> & myArguments) {
       //do whatever you want, with all the convenience of lists
    }
    
    foo({"arg1","arg2"});

    lista de inicializador FTW!

  4. 17

    En C++11 hay una manera de hacer de la variable argumento de plantillas que conducen a un muy elegante y seguro del tipo de manera de tener la variable argumento de funciones. Bjarne mismo da un buen ejemplo de printf uso de argumentos variable plantillas en el C++11FAQ.

    Personalmente, considero que esto es tan elegante que no me molestes con una variable argumento de la función en C++ hasta que el compilador tiene soporte para C++11 de argumentos variable plantillas.

  5. 15

    C-estilo variadic funciones son compatibles en C++.

    Sin embargo, la mayoría de las bibliotecas de C++ el uso de una alternativa modismo por ejemplo, mientras que el 'c' printf función toma la variable de argumentos de la c++ cout que utiliza el objeto << la sobrecarga de que las direcciones de tipo de seguridad y Tdas (quizás en el costo de la aplicación de la simplicidad).

  6. 13

    Aparte de varargs o sobrecarga, usted podría considerar la posibilidad de agregar tus argumentos en un std::vector u otros recipientes (std::map por ejemplo). Algo como esto:

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

    De esta manera se podría obtener el tipo de seguridad y el significado lógico de estos variadic argumentos sería evidente.

    Sin duda, este enfoque puede tener problemas de rendimiento, pero usted no debe preocuparse por ellos a menos que esté seguro de que usted no puede pagar el precio. Es una especie de un «Python» enfoque a la de c++ …

    • Limpiador sería no cumplir los vectores. En lugar de utilizar un argumento de plantilla, especificando la STL-estilo de la colección de iterar a través de ella mediante el argumento de empezar y terminar métodos. De esta manera usted puede usar std::vector<T>, c++11 std::array<T, N> std::initializer_list<T> o incluso hacer su propia colección.
    • Estoy de acuerdo, pero este es el tipo de elección que debe ser analizado para el tema en cuestión, para evitar el exceso de ingeniería. Dado que este es un asunto de la firma de la API, es importante entender el equilibrio entre la máxima flexibilidad y claridad de intención/usabilidad/maintanability etc.
  7. 12

    C++17 solución: completa tipo de seguridad + niza sintaxis de llamada

    Desde la introducción de variadic plantillas en C++11 y doblar las expresiones en C++17, es posible definir una plantilla de función que, en el llamado sitio, es que se puede llamar como si fuera un varidic función, pero con las ventajas de:

    • ser fuertemente tipo de seguro;
    • trabajo sin la información de tiempo de ejecución de la serie de argumentos, o sin el uso de un «stop» argumento.

    Aquí es un ejemplo de mezcla de tipos de argumento

    template<class... Args>
    void print(Args... args)
    {
        (std::cout << ... << args) << "\n";
    }
    print(1, ':', " Hello", ',', " ", "World!");

    Y otro con las de tipo partido para todos los argumentos:

    #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!");
                                                                                                  ^

    Más información:

    1. Variadic plantillas, también conocida como parámetro pack Parámetro pack(ya que C++11) – cppreference.com.
    2. Veces expresiones doblar la expresión(ya que C++17) – cppreference.com.
    3. Ver un completo programa de demostración en coliru.
    • un día enfermo espero poder leer template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
    • leer como «Esta cosa está habilitado sólo si Head y Tail... son la misma«, donde «son la misma» significa std::conjunction<std::is_same<Head, Tail>...>. Leer esta última definitinon como «Head es el mismo que todos los de Tail...«.
  8. 8

    La única manera es a través del uso de estilo de C variable de argumentos, como se describe aquí. Tenga en cuenta que esta no es una práctica recomendada, ya que no typesafe y propenso a errores.

    • Por propensos a errores, supongo que te refieres potencialmente muy peligroso? Especialmente cuando se trabaja con la confianza de entrada.
    • Sí, pero por el tipo de cuestiones de seguridad. Pensar en todos los posibles problemas que regular printf tiene: formato de las cadenas de la no coincidencia de argumentos pasados, y tal. printf usa la misma técnica, por CIERTO.
  9. 7

    No hay ningún estándar de C++ manera de hacer esto sin recurrir a la C-estilo varargs (...).

    Por supuesto, hay argumentos predeterminados ese tipo de «look» como número variable de argumentos según el contexto:

    void myfunc( int i = 0, int j = 1, int k = 2 );
    
    //other code...
    
    myfunc();
    myfunc( 2 );
    myfunc( 2, 1 );
    myfunc( 2, 1, 0 );

    Las cuatro de la función de llamadas myfunc con número variable de argumentos. Si no está dado, el valor predeterminado argumentos se utilizan. Tenga en cuenta sin embargo, que sólo se puede omitir los argumentos del final. No hay manera, por ejemplo, omitir i y sólo dan j.

  10. 4

    Es posible que desee sobrecarga o parámetros por defecto – definir la misma función con defecto de los parámetros:

    void doStuff( int a, double termstator = 1.0, bool useFlag = true )
    {
       //stuff
    }
    
    void doStuff( double std_termstator )
    {
       //assume the user always wants '1' for the a param
       return doStuff( 1, std_termstator );
    }

    Esto le permitirá llamar al método con uno de los cuatro diferentes llamadas:

    doStuff( 1 );
    doStuff( 2, 2.5 );
    doStuff( 1, 1.0, false );
    doStuff( 6.72 );

    … o usted podría estar buscando la v_args convenciones de llamada de C.

  11. 2

    Como han dicho otros, C-estilo varargs. Pero también puedes hacer algo similar con argumentos predeterminados.

    • Podría usted comentar?
    • Para obtener un ejemplo de cómo hacerlo con argumentos predeterminados, mira Zoltan la respuesta.
  12. 2

    Si sabe el rango de número de argumentos que serán proporcionados, siempre puedes usar algunos sobrecarga de funciones, como

    f(int a)
        {int res=a; return res;}
    f(int a, int b)
        {int res=a+b; return res;}

    y así sucesivamente…

  13. 1

    Utilizando variadic plantillas de ejemplo para reproducir console.log como se ve en JavaScript:

    Console console;
    console.log("bunch", "of", "arguments");
    console.warn("or some numbers:", 1, 2, 3);
    console.error("just a prank", "bro");

    Nombre de archivo por ejemplo,js_console.h:

    #include <iostream>
    #include <utility>
    
    class Console {
    protected:
        template <typename T>
        void log_argument(T t) {
            std::cout << t << " ";
        }
    public:
        template <typename... Args>
        void log(Args&&... args) {
            int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
            cout << endl;
        }
    
        template <typename... Args>
        void warn(Args&&... args) {
            cout << "WARNING: ";
            int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
            cout << endl;
        }
    
        template <typename... Args>
        void error(Args&&... args) {
            cout << "ERROR: ";
            int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
            cout << endl;
        }
    };
  14. 0

    También podríamos utilizar un initializer_list si todos los argumentos son const y del mismo tipo

    • ????? ¿A qué te refieres?
  15. 0

    Es posible ahora…el uso de impulsar cualquier y plantillas
    En este caso, los argumentos de tipo puede ser mezclado

    #include <boost/any.hpp>
    #include <iostream>
    
    #include <vector>
    using boost::any_cast;
    
    template <typename T, typename... Types> 
    void Alert(T var1,Types... var2) 
    { 
    
        std::vector<boost::any> a(  {var1,var2...});
    
        for (int i = 0; i < a.size();i++)
        {
    
        if (a[i].type() == typeid(int))
        {
            std::cout << "int "  << boost::any_cast<int> (a[i]) << std::endl;
        }
        if (a[i].type() == typeid(double))
        {
            std::cout << "double "  << boost::any_cast<double> (a[i]) << std::endl;
        }
        if (a[i].type() == typeid(const char*))
        {
            std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
        }
        //etc
        }
    
    } 
    
    
    void main()
    {
        Alert("something",0,0,0.3);
    }

Dejar respuesta

Please enter your comment!
Please enter your name here