La especificación de un tipo para todos los argumentos que se pasan a variadic función o variadic función de la plantilla w/out utilizando la matriz, vector, estructuras, etc?

Estoy creando una función (posiblemente función miembro, pero no importa… tal vez sí?) que necesita para aceptar un número indeterminado de argumentos, pero quiero que todos sean del mismo tipo. Sé que podría pasar en una matriz o vector, pero quiero ser capaz de aceptar la lista de argumentos directamente sin extra estructura o incluso extra entre paréntesis. No parece variadic funciones por sí mismos son typesafe, y yo no estaba seguro de cómo ir sobre esto w/variadic plantilla de funciones. Aquí es básicamente lo que estoy buscando (más que probable que no el código correcto, y totalmente no para 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<Maiden... Maidens> 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)
;

Intentado un par de cosas similares a las anteriores ya, no dados. Sugerencias? Obvio descuidos yo hubiese hecho? Sé que puede no ser un gran negocio para hacer esto en su lugar:

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

pero me gustaría mucho más que ser capaz de hacerlo de la primera manera, si es posible.

10 Kommentare

  1. 31

    Puede aceptar los argumentos de la variadic plantilla y deja validación de tipos de comprobar la validez más tarde, cuando se convierten.

    Puede comprobar la convertibilidad en la función de la interfaz de nivel a pesar de que, para hacer uso de la resolución de sobrecarga para rechazar completamente equivocados argumentos, por ejemplo, mediante el uso de SFINAE

    template<typename R, typename...> struct fst { typedef R type; };
    
    template<typename ...Args>
    typename fst<void, 
      typename enable_if<
        is_convertible<Args, ToType>::value
      >::type...
    >::type 
    f(Args...);

    Para su uso-caso de que si usted sabe los pasos para pasar de una std::array<> a su dragon_list_t entonces ya ha resuelto a pesar de que según la primera opción («convertir» más adelante»):

    template<typename ...Items>
    dragon_list_t make_dragon_list(Items... maidens) {
        std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};
        //here be dragons
    }

    Si se combina esto con el anterior is_convertible enfoque tiene una rechazar-principios de la plantilla de la que también hace a la resolución de sobrecarga en los argumentos y las rechaza si no es aplicable.

    • +1 por dar OP exactamente lo que pedía, aunque sea para mejor o peor
    • No he jugado con el variadic plantillas suficiente para saber que si los primeros convertible se llama, pero +1 std::matriz de conversión, no pensar en eso.
    • Hmm, creo que la rechazan-principios de la plantilla no está bien para el caso general. Si el Dragón era una clase base, entonces el primer argumento (o todos tus argumentos), puede ser una clase derivada, y para ello no convertibles en cada uno de los otros. Cuando el dragón es una enumeración, podría ser la misma cosa si tu primera clase es convertibles a tipo entero, pero no de un tipo entero en sí mismo. Creo que confiar en la sobrecarga es más extensible.
    • el rechazar-principios de la plantilla sólo comprueba la convertibilidad de los argumentos a ToType (Dragon), no de los argumentos para cada uno de los otros. Así D1->B está activada y D2->B también, pero D1->D2 no está activada.
    • Por curiosidad, si el convertir-la última opción fue implementado en una biblioteca que otro dev estaba usando, qué tipo de error se tendría que ver si no tiene la aplicación para make_dragon_list, sólo un encabezado, y que se alimenta el mal tipos? Tendría que incluso no ser una preocupación válida, o sería un motivo a favor de la rechazan-principios de opción?
    • yo no tengo ni idea. Realmente depende de la muy de código dentro de la plantilla. Pero no será agradable creo

  2. 16

    Si no uso template en el parámetro no en el pack de la variadic función de resolver para tener todos los argumentos del mismo tipo.

    He aquí un ejemplo de una extendida max función que sólo acepta ints (o tipos convertible a int).

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

    Explicación: Cuando desembale el argumento pack (args...) el compilador busca el mejor sobrecarga. Si el paquete había un solo parámetro, a continuación, el mejor candidato es maximum(int) por lo que el único parámetro que se debe y de tipo int (o convertible a int). Si hay más de uno de los elementos en el paquete, a continuación, el único candidato es maximum(int, typename...) así que el primer argumento debe ser de tipo int (o convertible a int). Es fácil de demostrar por inducción que todos los tipos en el envase debe ser de un tipo convertible en int).

    • Interesante… no se puede decir que entiendo por qué esto funciona, cualquier info/enlaces en eso?
  3. 11

    Ya has incluido el de C++0x etiqueta, la respuesta obvia sería la de buscar inicializador de las listas de. Una lista de inicializador le permite especificar un número de argumentos a un cto r que se convierte automáticamente en una única estructura de datos para el procesamiento por el cto r.

    Primaria (exclusivo?) el uso es exactamente el tipo de situación en la que usted ha mencionado, pasando por una serie de argumentos del mismo tipo para el uso en la creación de algún tipo de lista/matriz/otros colección de objetos. Va a ser apoyado por (por ejemplo) std::vector, así que usted podría utilizar algo como:

    std::vector<dragon> dragons_to_slay{Eunice, Helga, Aida};

    para crear un vector de tres dragon objetos. La mayoría (todos?) de las otras colecciones se incluyen en el mismo, así que si usted realmente insistir en una lista de los dragones usted debería ser capaz de obtener muy fácilmente.

    • Ni siquiera creo que buscar en init listas, gracias por la sugerencia. Todavía masticando en todas estas respuestas :\
  4. 5

    Recientemente he necesaria para limitar un parámetro pack para un solo tipo, o al menos convertible a ese tipo. Terminé de encontrar de otra manera:

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

    La cosa que me gustó de esta solución es que no se puede aplicar check_all a otros rasgos también.

  5. 4

    Una propuesta reciente, Homogénea variadic funciones, aborda esta haciendo algo así como su primer construir legal. Excepto, por supuesto, utilizar el parámetro pack tiene el nombre. También la sintaxis exacta no parece muy concreta todavía.

    Así, bajo la propuesta será legal (puede ver una similar de la construcción en el párrafo «la plantilla introductor» en el papel):

    dragon_list_t make_dragon_list(Maiden... maidens) {
        //here be dragons
    }
    • no puede entender por qué esto ya no existe en el idioma! quiero que este todo el tiempo.
  6. 2

    Aunque la pregunta está etiquetada C++11, creo que C++17 + conceptos de solución que cabe añadir viendo como a pesar de que ahora hay soporte en GCC y otros seguirán pronto.

    primero definir un concepto simple

    class mytype{};
    
    template<typename T>
    concept bool MyType = std::is_same<T, mytype>::value;

    a continuación, utilice simplemente variadic los parámetros de la plantilla

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

    Mucho más fácil con el advenimiento de los conceptos!

    • Conceptos que hice en C++2a (hasta ahora) no incluyen este. Ver aquí.
  7. 1

    Realmente depende de lo que usted está tratando de implementar, exactamente.

    Generalmente enum indica el tiempo de ejecución de los subtipos de una clase particular, o de una unión discriminada (boost::variante). Pero en este caso, usted desea pasar el enum directamente. Por otra parte, usted tiene un número limitado de posibles valores, y cada llamada de función forma un subconjunto. Realmente lo que se representa es un subconjunto, no varios parámetros en todos.

    La mejor forma de representar un subconjunto de un conjunto finito es un bitset. Grandes conjuntos deben utilizar std::bitset; los pequeños conjuntos puede 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, ya que al parecer se desea controlar la variación en tiempo de compilació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 de manera automática utilizando una operator+ sobrecargado en el enum tipo. Pero no estoy seguro de que estoy en el camino correcto en todo.

    • Usted puede compilar en tiempo bitshift, que es, enum { first_value = 1, second_value = 1 << 1, third_value = 1 << 2 };
    • Me gustaría escribir de esa manera, pero usted se sorprenderá de cómo muchos de los lectores nunca han visto que el operador antes.
    • Yo tengo, pero por curiosidad, ¿existe alguna preocupación acerca de los bits de peso? Sé que probablemente en la oscuridad. No sé qué Cpu que sería un problema, y es que no se como estoy tratando de construir algo que el 100% de la cruz-plataforma o nada. Sólo por curiosidad.
    • Bueno, ya no te importa lo que está establecido el bit, sólo que cada indicador corresponde a un bit, entonces no hay ninguna razón por la que el endianness o portabilidad debe romper el código. Como siempre que no se trate de establecer más bits de los que existen en los tipos de respaldo, estás bien.
    • No, un bitset codificación es simplemente un número como cualquier otro, por lo que (Eunice+Beatrice+Helga) = 1+2+8 = 11 no es más endianness sensible que simplemente escribiendo el número 11.
  8. 1

    Me gustaría tratar de mantener las cosas simples, y la solución más sencilla que se me ocurre es usar un viejo y simple vector. Mediante el uso de C++0x características que usted puede conseguir una sintaxis que es similar (aunque no exactamente) lo que usted desea:

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

    En resumen, debe, probablemente, sólo crear un vector. No es que se mucho sobre la cabeza, especialmente si usas algo como boost::list_of o C++0x la lista de inicializador. La sintáctica sobrecarga es mínima, y es más flexible (se podría pasar lista con un número de argumentos conocidos sólo en tiempo de ejecución).

    Si usted realmente desea, usted podría utilizar variadic los parámetros de la plantilla 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<typename... Args>
    dragon_list_t make_dragon_list( Args... args )
    {
      dragon_list_t dragon_list;
      make_dragon_list_internal( &dragon_list, args... );
      return dragon_list;
    }

    Es typesafe, y extensible (se podría hacer de esto tomar las cosas de otros de los dragones, si te apetece).

    • Err, y por typesafe, quiero decir, como typesafe como el tipo subyacente, que no de la clase enum no es mucho.
  10. 0

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

    template <class...>
    struct IsAllSame {};
    
    template <class T, class B1>
    struct IsAllSame<T, B1> {
      static constexpr const bool kValue = std::is_same<T, B1>::value;
    };
    
    template <class T, class B1, class... Bn>
    struct IsAllSame<T, B1, Bn...> {
      static constexpr const bool kValue =
          IsAllSame<T, B1>::kValue ? IsAllSame<T, Bn...>::kValue : false;
    };
    
    IsAllSame<int>::kValue == true
    IsAllSame<bool, int>::kValue == false
    IsAllSame<bool, int, int>::kValue == false

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea