Especificando un tipo para todos los argumentos pasados ​​a la función variadic o a la función de plantilla variadica w / out usando array, vector, structs, etc.

Estoy creando una función (posiblemente función de miembro, no es que importe … ¿quizás sí?) Que necesita aceptar un número desconocido de argumentos, pero quiero que todos sean del mismo tipo. Sé que podría pasar una matriz o un vector, pero quiero poder aceptar la lista de argumentos directamente sin estructura adicional o incluso con corchetes adicionales. No parece que las funciones variadas en sí mismas sean seguras para el tipo, y no estaba seguro de cómo hacerlo con las funciones de plantillas variadas. Aquí es esencialmente lo que estoy buscando (más que probable que no sea el código correcto, y no con el propósito de obtener listas de dragones, lol):

//typedef for dragon_list_t up here somewhere. enum Maiden { Eunice , Beatrice , Una_Brow , Helga , Aida }; dragon_list_t make_dragon_list(Maiden...) { //here be dragons } 

O

 template dragon_list_t make_dragon_list(Maidens...) { //here be dragons } 

USO

 dragon_list_t dragons_to_slay = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida) ; 

Intenté algunas cosas similares a las anteriores, sin dados. Sugerencias? Descuidos obvios que pude haber hecho? Sé que puede no ser un gran problema hacer esto en su lugar:

 dragon_list_t make_dragon_list(std::array maidens) { //here be dragons. } dragon_list_t dragons_to_slay = make_dragon_list({Maiden.Eunice, Maiden.Helga, Maiden.Aida}) ; 

pero preferiría ser capaz de hacerlo de la primera manera si es posible.

Puede aceptar los argumentos de la plantilla variadica y dejar que la verificación de tipo compruebe la validez más adelante cuando se conviertan.

Sin embargo, puede verificar la convertibilidad en el nivel de la interfaz de la función, para hacer uso de la resolución de sobrecarga para rechazar por completo argumentos erróneos, utilizando SFINAE

 template struct fst { typedef R type; }; template typename fst::value >::type... >::type f(Args...); 

Para su caso de uso, si conoce los pasos para pasar de std::array<> a su dragon_list_t entonces ya lo ha resuelto de acuerdo con la primera opción anterior (“convert-later”):

 template dragon_list_t make_dragon_list(Items... maidens) { std::array arr = {{ maidens ... }}; // here be dragons } 

Si combina esto con el enfoque is_convertible anterior, tiene una plantilla de rechazo temprano que también sobrecarga la resolución de los argumentos y los rechaza si no es aplicable.

Si no usa la template en el parámetro que no está en el paquete, la función variadic resolverá tener todos los argumentos del mismo tipo.

Aquí hay un ejemplo para una función max extendida que solo acepta int s (o tipos convertibles a int ).

 int maximum(int n) // last argument must be an `int` { return n; } template int maximum(int n, Args... args) // first argument must be an int { return std::max(n, maximum(args...)); } 

Explicación: cuando descomprime el paquete de argumentos ( args... ) el comstackdor busca la mejor sobrecarga. Si el paquete tenía solo un parámetro, entonces el único candidato es el maximum(int) por lo que el único parámetro debe ser y de tipo int (o convertible a int ). Si hay más de un elemento en el paquete, entonces el único candidato es el maximum(int, typename...) por lo que el primer argumento debe ser de tipo int (o convertible a int ). Es simple probar por inducción que todos los tipos en el paquete deben ser de un tipo convertible a int ).

Dado que ha incluido la etiqueta C ++ 0x, la respuesta obvia sería buscar listas de inicializadores . Una lista de inicializadores le permite especificar una cantidad de argumentos para un controlador que se convertirá automáticamente a una única estructura de datos para que el procesador los procese.

Su uso primario (¿exclusivo?) Es exactamente para el tipo de situación que ha mencionado, pasando una cantidad de argumentos del mismo tipo para usar al crear algún tipo de lista / matriz / otra colección de objetos. Será compatible con (por ejemplo) std::vector , por lo que podría usar algo como:

 std::vector dragons_to_slay{Eunice, Helga, Aida}; 

para crear un vector de tres objetos de dragon . La mayoría (¿todas?) De las otras colecciones incluirán lo mismo, así que si realmente insistes en una lista de dragones, deberías ser capaz de conseguir eso también con bastante facilidad.

Recientemente necesité restringir un paquete de parámetros para que sea solo un tipo, o al menos convertible a ese tipo. Terminé encontrando otra manera:

 #include  #include  template  class Trait, typename Head, typename ...Tail> struct check_all { enum { value = Trait::value && check_all::value }; }; template  class Trait, typename Head> struct check_all { enum { value = Trait::value }; }; template  struct foo { // Using C++11 template alias as compile time std::bind template  using Requirement = std::is_convertible; static_assert(check_all::value, "Must convert to double"); }; int main() { foo(); foo(); // Errors, no conversion } 

Lo que me gustó de esta solución es que puedo aplicar check_all a otros rasgos también.

Realmente depende de lo que intentas implementar, exactamente.

Por lo general, enum indica subtipos de tiempo de ejecución de una clase en particular, o una unión discriminada (boost :: variant). Pero en este caso, quiere pasar la enum directamente. Además, tiene un conjunto limitado de valores posibles, y cada llamada de función forma un subconjunto. Realmente lo que estás representando es un subconjunto, no varios parámetros en absoluto.

La mejor forma de representar un subconjunto de un conjunto finito es un conjunto de bits. Los grandes conjuntos deben usar std::bitset ; los conjuntos pequeños pueden usar unsigned long .

 enum Maiden_set { Eunice = 1, , Beatrice = 2 , Una_Brow = 4 , Helga = 8 , Aida = 16 }; dragon_list_t make_dragon_list(Maiden_set) { //here be dragons } make_dragon_list( Eunice + Beatrice + Helga ); 

o, dado que parece querer manejar la variación en tiempo de comstackción,

 template< int Maidens > // parameter is not a Maiden_set because enum+enum=int dragon_list_t make_dragon_list() { //here be dragons } make_dragon_list< Eunice + Beatrice + Helga >(); // + promotes each enum to int 

Debería ser posible generar las potencias de 2 automáticamente usando un operator+ sobrecargado en el tipo de enum . Pero no estoy seguro de estar en el camino correcto.

Intentaría mantener las cosas simples, y la solución más simple que puedo pensar es simplemente usar un viejo vector simple. Al usar las funciones de C ++ 0x puede obtener una syntax que es similar a (aunque no exactamente) lo que desea:

 void foo( std::vector const & v ) { std::copy( v.begin(), v.end(), std::ostream_iterator(std::cout, " ") ); } int main() { foo({ 1, 2, 3, 4, 5, 6 }); // note the extra {} } 

En resumen, probablemente solo deberías crear un vector. No es demasiada sobrecarga, especialmente si usa algo como boost :: list_of o la lista de inicializadores de C ++ 0x. La sobrecarga sintáctica es mínima y es más flexible (podría pasar la lista con una cantidad de argumentos conocidos solo en tiempo de ejecución).

Si realmente lo deseaba, podría usar parámetros de plantilla variadic para hacer esto:

 // Using pass-by-value since I'm assuming it is primitive: template< typename T, typename... Args> void make_dragon_list_internal( dragon_list_t* dragon_list, T t, Args... args ) { // add T to dragon_list. make_dragon_list_internal( dragon_list, args... ); } void make_dragon_list_internal( dragon_list_t* dragon_list ) { // Finalize dragon_list. } template dragon_list_t make_dragon_list( Args... args ) { dragon_list_t dragon_list; make_dragon_list_internal( &dragon_list, args... ); return dragon_list; } 

Es seguro y extensible (puedes hacer que tome cosas que no sean dragones, si te apetece).

Aunque la pregunta está etiquetada como C ++ 11, creo que una solución de conceptos C ++ 17 + valdría la pena agregarlo, ya que ahora hay compatibilidad en GCC y otros seguirán pronto.

primero define un concepto simple

 class mytype{}; template concept bool MyType = std::is_same::value; 

entonces simplemente use parámetros de plantilla variadic

 template void func(Args &&... args){ // do something here } 

¡Mucho más fácil con la llegada de los conceptos!

Creo que el siguiente código es útil para su caso:

 template  struct IsAllSame {}; template  struct IsAllSame { static constexpr const bool kValue = std::is_same::value; }; template  struct IsAllSame { static constexpr const bool kValue = IsAllSame::kValue ? IsAllSame::kValue : false; }; IsAllSame::kValue == true IsAllSame::kValue == false IsAllSame::kValue == false