¿Número variable de argumentos en C++?

12 minutos de lectura

¿Numero variable de argumentos en C
Nunos

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

  • En este momento con C++11 las respuestas a esta pregunta diferirían mucho

    – K-ballo

    8 de enero de 2013 a las 20:07

  • @K-ballo También agregué ejemplos de C ++ 11 ya que una pregunta reciente hizo lo mismo recientemente y sentí que necesitaba uno para justificar cerrarlo stackoverflow.com/questions/16337459/…

    – Shafik Yaghmour

    3 mayo 2013 a las 13:46

  • Añadido pre C++11 opciones a mi respuesta también, por lo que ahora debería cubrir la mayoría de las opciones disponibles.

    – Shafik Yaghmour

    2 de diciembre de 2013 a las 18:32

  • @K-ballo, no hay forma de hacerlo en C++ en caso de que necesite un tipo de argumento forzado … sin construcción como foo (int … valores) :/ Si no le importan los tipos, entonces sí, plantillas variadas en C++ 11 funciona muy bien

    – Lobo gris

    07/08/2016 a las 21:03

  • stackoverflow.com/a/29326784/4361073

    – parasrish

    24 de noviembre de 2017 a las 13:08

¿Numero variable de argumentos en C
Shafik Yaghmour

En C++11 tienes dos nuevas opciones, como el Argumentos variados página de referencia en el Sección de alternativas estados:

  • Las plantillas Variadic también se pueden usar para crear funciones que toman un número variable de argumentos. Suelen ser la mejor opción porque no imponen restricciones sobre los tipos de argumentos, no realizan promociones integrales ni de punto flotante y son de tipo seguro. (desde C++11)
  • Si todos los argumentos de las variables comparten un tipo común, std::initializer_list proporciona un mecanismo conveniente (aunque con una sintaxis diferente) para acceder a los argumentos de las variables.

A continuación se muestra un ejemplo que muestra ambas alternativas (verlo en vivo):

#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 estás usando gcc o clang podemos usar el PRETTY_FUNCTION variable magica para mostrar la firma de tipo de la función que puede ser útil para comprender lo que está sucediendo. Por ejemplo usando:

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

daría como resultado int siguiente para funciones variádicas en el ejemplo (verlo en vivo):

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 puedes usar FUNCIÓN.

Actualizar antes de C++ 11

Pre C++11 la alternativa para std::initializer_list sería estándar::vector o uno de los otros 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 plantillas variadas sería funciones variadicas aunque no lo son tipo seguro y en general propenso a errores y puede ser inseguro de usar pero la única otra alternativa potencial sería usar argumentos predeterminados, aunque eso tiene un uso limitado. El siguiente ejemplo es una versión modificada del código de muestra en la referencia vinculada:

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

Utilizando funciones variadicas también viene con restricciones en los argumentos que puede pasar, que se detalla en el borrador del estándar C++ en la sección 5.2.2 Llamada de función párrafo 7:

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

  • Es tuyo typename contra class uso por encima de intencional? Si es así, por favor explique.

    – Kevinarpe

    30 de septiembre de 2016 a las 10:08

  • @kevinarpe no es intencional, aunque no cambia nada.

    – Shafik Yaghmour

    30 de septiembre de 2016 a las 12:34

  • Tu primer enlace probablemente debería ser para probablemente para en.cppreference.com/w/cpp/language/variadic_arguments en lugar de.

    – Alexei Romanov

    06/04/2018 a las 12:00

  • ¿Es posible hacer una función tomando un initializer_list recursivo?

    – 463035818_no_es_un_número

    26/10/2018 a las 22:30

1647562751 632 ¿Numero variable de argumentos en C
guillermo

Probablemente no debería, y probablemente pueda hacer lo que quiera de una manera más segura y sencilla. Técnicamente, para usar un número variable de argumentos en C, incluye stdarg.h. De ahí obtendrás el va_list tipo así como tres funciones que operan sobre él llamadas 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 preguntas, esto es un desastre. Se ve mal, no es seguro y está lleno de detalles técnicos que no tienen nada que ver con lo que estás tratando de lograr conceptualmente. En su lugar, considere usar sobrecarga o herencia/polimorfismo, patrón constructor (como en operator<<() en flujos) o argumentos predeterminados, etc. Todos estos son más seguros: el compilador llega a saber más sobre lo que está tratando de hacer, por lo que hay más ocasiones en las que puede detenerlo antes de que se vuele la pierna.

  • Presumiblemente, no puede pasar referencias a una función varargs porque el compilador no sabría cuándo pasar por valor y cuándo por referencia, y porque las macros de C subyacentes no necesariamente sabrían qué hacer con las referencias; ya existen restricciones sobre qué puede pasar a una función C con argumentos variables debido a cosas como las reglas de promoción.

    –Jonathan Leffler

    1 de noviembre de 2009 a las 19:24

  • ¿Es necesario proporcionar al menos un argumento antes de la ... ¿sintaxis?

    – Lazer

    23 de junio de 2010 a las 9:54

  • @Lazer no es un requisito de idioma o biblioteca, pero la biblioteca estándar no le brinda los medios para saber la longitud de la lista. Necesita que la persona que llama le brinde esta información o, de lo contrario, de alguna manera lo resolverá usted mismo. En el caso de printf()por ejemplo, la función analiza el argumento de cadena en busca de tokens especiales para determinar cuántos argumentos adicionales debe esperar en la lista de argumentos variables.

    – Guillermo Tell

    23 de junio de 2010 a las 21:33

  • probablemente deberías usar <cstdarg> en C++ en lugar de <stdarg.h>

    – nueva cuenta

    08/01/2013 a las 21:05


  • El número variable de argumentos es excelente para la depuración o para funciones/métodos que llenan una matriz. También es genial para muchas operaciones matemáticas, como máximo, mínimo, suma, promedio… No es un lío cuando no te metes con él.

    – Tomáš Zato – Reincorporar a Mónica

    5 de diciembre de 2014 a las 20:49

¿Numero variable de argumentos en C
YSC

Una solución de C++17: seguridad de tipos completa + buena sintaxis de llamada

Desde la introducción de plantillas variádicas en C++11 y expresiones de plegado en C++17, es posible definir una función de plantilla que, en el sitio de la persona que llama, se puede llamar como si fuera una función varidica pero con las ventajas de :

  • sea ​​fuertemente seguro;
  • trabajar sin la información de tiempo de ejecución del número de argumentos, o sin el uso de un argumento de “detención”.

Aquí hay un ejemplo para tipos de argumentos mixtos

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

Y otro con coincidencia de tipo forzada 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. Plantillas variádicas, también conocidas como paquete de parámetros Paquete de parámetros (desde C++ 11) – cppreference.com.
  2. Expresiones de pliegue expresión de pliegue (desde C++ 17) – cppreference.com.
  3. Ver un demostración completa del programa en coliru.

  • algún día espero poder leer template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>

    – Eladian

    19 de abril de 2018 a las 7:11

  • @Eladian lo leyó como “Esto está habilitado solo si Head y Tail... son lo mismo“, donde “son lo mismo” medio std::conjunction<std::is_same<Head, Tail>...>. Lea esta última definición como “Head es igual a todos Tail...“.

    – YSC

    20 de abril de 2018 a las 16:09

  • ¿Podemos hacer un bucle? args?

    – Abbas Ebadian

    30 de junio de 2021 a las 12:25

  • @AbbasEbadian Sí, pero esa es una pregunta en sí misma.

    – YSC

    hace 12 horas

en c ++ 11 puedes hacer:

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

foo({"arg1","arg2"});

inicializador de lista FTW!

1647562752 376 ¿Numero variable de argumentos en C
De todo género

En C++ 11 hay una manera de hacer plantillas de argumentos variables que conducen a una forma realmente elegante y segura de tener funciones de argumentos variables. El propio Bjarne da un buen ejemplo de printf usando plantillas de argumentos variables en el Preguntas frecuentes sobre C++11.

Personalmente, considero esto tan elegante que ni siquiera me molestaría con una función de argumento variable en C++ hasta que el compilador sea compatible con las plantillas de argumentos variables de C++11.

  • @donlan: si usa C ++ 17, puede usar expresiones de plegado para simplificar mucho las cosas en algunos casos (piense creativamente aquí, puede usar el , operador con expresiones de plegado). De lo contrario, no lo creo.

    – Omnifaro

    12 de enero de 2020 a las 17:30

  • Buen artículo al que se hace referencia. Cuando comencé a leer la tupla de la plantilla de clase en la sección “Podemos construir un tipo variádico”, de repente pensé: “¡Dios mío! Esto es programación Lisp, en tiempo de compilación, en C++…” .. LOL ..

    – Razzle

    18/01/2021 a las 20:30


Las funciones variádicas de estilo C se admiten en C++.

Sin embargo, la mayoría de las bibliotecas de C++ utilizan un idioma alternativo, por ejemplo, mientras que el 'c' printf función toma argumentos variables el c++ cout usos de objetos << sobrecarga que aborda la seguridad de tipo y los ADT (quizás a costa de la simplicidad de implementación).

  • @donlan: si usa C ++ 17, puede usar expresiones de plegado para simplificar mucho las cosas en algunos casos (piense creativamente aquí, puede usar el , operador con expresiones de plegado). De lo contrario, no lo creo.

    – Omnifaro

    12 de enero de 2020 a las 17:30

  • Buen artículo al que se hace referencia. Cuando comencé a leer la tupla de la plantilla de clase en la sección “Podemos construir un tipo variádico”, de repente pensé: “¡Dios mío! Esto es programación Lisp, en tiempo de compilación, en C++…” .. LOL ..

    – Razzle

    18/01/2021 a las 20:30


1647562752 519 ¿Numero variable de argumentos en C
francesco

Además de los varargs o la sobrecarga, podría considerar agregar sus argumentos en un std::vector u otros contenedores (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 forma ganaría seguridad de tipo y el significado lógico de estos argumentos variados sería evidente.

Seguramente este enfoque puede tener problemas de rendimiento, pero no debe preocuparse por ellos a menos que esté seguro de que no puede pagar el precio. Es una especie de enfoque “Pythonic” para c ++ …

  • Limpiador sería no hacer cumplir los vectores. En su lugar, use un argumento de plantilla que especifique la colección de estilo STL y luego itere a través de ella usando los métodos de inicio y fin del argumento. De esta forma, puede usar std::vector, std::array de c++11, std::initializer_list o incluso crear su propia colección.

    – Jens Åkerblom

    29 de marzo de 2013 a las 11:26


  • @JensÅkerblom Estoy de acuerdo, pero este es el tipo de elección que debe analizarse para el problema en cuestión, para evitar el exceso de ingeniería. Dado que se trata de una cuestión de firma API, es importante comprender el equilibrio entre la máxima flexibilidad y la claridad de la intención/usabilidad/mantenibilidad, etc.

    – Francisco

    30 de marzo de 2013 a las 14:18

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad