2012-04-06 19 views
19

Ya conozco la forma stdarg.h de tener una función con argumentos de variables en C++ como se describe here por ejemplo. También sé C++ 11 estándar tiene plantillas variadic como se explica here.Una función con número variable de argumentos con tipos conocidos, el camino de C++ 11

Pero en los dos esquemas mencionados anteriormente no sabemos (y no podemos forzar) los tipos de argumento en tiempo de compilación afaik. Lo que estoy buscando es pasar argumentos de tipos conocidos a una función. Creo que esto se puede hacer porque lo leí here:

plantillas variadic, que también pueden ser utilizados para crear las funciones que toman un número variable de argumentos, son a menudo la mejor opción, ya que no imponen restricciones a la tipos de argumentos, no realiza promociones integrales y de punto flotante, y son de tipo seguro.

¿Es posible? Si es así, ¿cómo puedo hacer esto?

Respuesta

30

Es sencillo escribir una función con plantillas variadas, que aceptan una cantidad arbitraria de argumentos. La única diferencia con el patrón general es que un tipo concreto se usa como primer argumento (encabezado), en lugar de un parámetro de plantilla. El siguiente ejemplo muestra una función foobar, que acepta un número arbitrario de cadenas.

// used for end of recursion - and for the empty arguments list 
void foobar() { } 

template <typename ...Tail> 
void foobar(const std::string& head, Tail&&... tail) 
{ 
    // do something with head 
    std::cout << head << '\n'; 
    // call foobar recursively with remaining arguments 
    foobar(std::forward<Tail>(tail)...); 
} 

foobar("Hello", "World", "..."); 

Personalmente, prefiero usar std::initializer_list en lugar de plantillas variadic. Porque las plantillas variadic son más complejas y requieren experiencia adicional. Con std::initializer_list, que podría tener este aspecto:

void foobar(std::initializer_list<std::string> values) 
{ 
    for (auto& value : values) { 
     // do something with value 
     std::cout << value << '\n'; 
    } 
} 

foobar({ "Hello", "World", "...", }); 

Desafortunadamente, se requiere que las llaves adicionales al usar std::initializer_list con funciones regulares. No son necesarios para los constructores, si se usa la nueva sintaxis del inicializador.

Editar: Reescribió la respuesta según los comentarios. En particular, he cambiado el orden de las dos soluciones/ejemplos.

+1

Creo que fue todo sobre mi malentendido. Sabía de la segunda manera, pero siempre pensé que no había control sobre el tipo de parámetros. Esto no es verdad. porque la función toma un primer parámetro de un tipo conocido (cadena aquí), está forzosamente implícitamente tener sus parámetros de ese tipo. Gracias. – melmi

+2

Es posible que desee mover primero la segunda caja de códigos. Es una muy buena solución al problema, mientras que la versión 'initializer_list' no lo es. Iba a publicar algo grande y complejo con SFINAE y tal, pero esto es mucho más razonable. –

+0

@nosid en realidad puede evitar el uso de llaves si se mezclan tanto std :: initializer_list como el enfoque de plantilla variadic, al crear una envoltura de plantilla variadica alrededor de la versión std :: initializer_list, como esta: [ver en coliru] (http: //coliru.stacked-crooked.com/a/4baef67192a0310c). –

2

Si los parámetros variables son todos de un tipo, puede cambiar la firma de la función para tomar una matriz de esos tipos en lugar de usar el '...'.

+0

Una matriz de un tipo necesita iteración, mientras que el uso de funciones variadas proporciona un mecanismo en línea de tiempo de compilación. Puede decir que el bucle en una matriz no es tan lento, pero considérelo en otros bucles grandes múltiples. – melmi

Cuestiones relacionadas