C++ parcial de la plantilla de la especialización en combinación con std::is_base_of y std::enable_if

Digamos que tengo una de dos clases: Serializable y Printable.

Por lo que una simple plantilla de función que acepta todas las clases derivadas de Printable podría parecerse a:

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

Sin embargo, si yo quiero también aceptar todas las clases derivadas de Serializable mientras que todavía tiene el control sobre el cuerpo de la función, esto obviamente, no funciona:

template <class T, class B = Printable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

template <class T, class B = Serializable, class = typename std::enable_if<std::is_base_of<B,     T>::value>::type>
void print(T value) {
    cout << value << endl;
}

//Error: Redefinition of ...

Así que supuse que el resto de soluciones para este problema son especializaciones de plantilla.

Pero no puedo averiguar, ¿cómo puedo especializar una plantilla en combinación con std::is_base_of y std::enable_if.

Espero que alguien está dispuesto a ayudarme!

  • Así que si la persona que llama no se especifica nada para B desea T se derivan de Printable o derivados de Serializable?
  • Parece que simplemente podría utilizar dos no-plantilla de sobrecargas: void print(const Printable& value);, void print(const Serializable& value);.
  • Yo podría, pero yo la necesito como una función de la plantilla.
  • Por qué plantilla es necesaria?
  • No, class B = ... no estaba destinado a ser opcional, me acaba de agregar para mejorar la legibilidad.
  • Razones de velocidad. Debe ser pasado por referencia, finalmente, este es sólo un ejemplo.
  • Una plantilla de función no puede ser parcialmente especializados.
  • No necesita ser. Quiero especializar la cosa entera.
  • ¿Qué quieres decir con «se especializan toda la cosa»? Una completa especialización tiene una plantilla vacía de argumentos de la lista: template<> void print<some concrete types>(... no Hay lugar para insertar std::enable_if.
  • Esta no es la especialización, es la sobrecarga y el uso de SFINAE, recorte la ambigua sobrecargas.
  • Esa es la razón por la que hice esta pregunta.

InformationsquelleAutor Tim | 2013-06-19

3 Kommentare

  1. 22

    Trate de un operador lógico:

    std::enable_if<std::is_base_of<Serializable, T>::value ||
                   std::is_base_of<Printable, T>::value>::type

    Puede escribir fácilmente un variadic plantilla como:

    is_base_of_any<T, Printable, Serialiable, Googlable, Foobarable>::value

    Por ejemplo:

    template <typename T, typename ...> struct is_base_of_any : std::true_type {};
    
    template <typename T, typename Head, typename ...Rest>
    struct is_base_of_any<T, Head, Rest...>
    : std::integral_constant<bool, std::is_base_of<T, Head>::value ||
                                   is_base_of_any<T, Rest...>::value>
    { };

    Si quieres diferentes implementaciones:

    template <bool...> struct tag_type {};
    
    template <typename T>
    void foo(T, tag_type<true, false>) { }   //for Printable
    
    template <typename T>
    void foo(T, tag_type<false, true>) { }   //for Serializable
    
    template <typename T>
    void foo(T x)
    {
        foo(x, tag_type<std::is_base_of<Printable, T>::value,
                        std::is_base_of<Serializable, T>::value>());
    }

    La última de sobrecarga (el «cara al usuario» una) probablemente debería estar dotado de las anteriores enable_if para no crear demasiado muchos sobrecarga de los candidatos.

    Probablemente, usted puede también hacer una variadic template <typename ...Bases> con una etiqueta como:

    tag_type<std::is_base_of<Bases, T>::value...>
    • Entiendo, pero yo quiero ser capaz de controlar el cuerpo de cada función, dependiendo de la clase base.
    • Ah OK, ya veo. En ese caso, usted necesita separar las sobrecargas y un despacho de función auxiliar; algún tipo de etiqueta de envío diría yo.
    • Gracias. Voy a mirar en eso!
    • He editado el post y añadido un esqueleto de enfoque.
    • class ControlMeHarder: public Serializable, public Printable {};.
    • Quantum sobrecarga!

  2. 21

    Un poco menos de la maquinaria que Kerrek la respuesta, pero me temo que no hay más legible:

    template <class T, typename std::enable_if<std::is_base_of<Printable, T>::value>::type* = nullptr>
    void print(const T& value) {
        std::cout << "printable(" << &value << ")\n";
    }
    
    template <class T, typename std::enable_if<std::is_base_of<Serializable, T>::value>::type* = nullptr>
    void print(const T& value) {
        std::cout << "serializable(" << &value << ")\n";
    }

    Ver vivo en ideone.

    • Esta realidad es la mejor solución para mi problema, ya que el código máquina generado. Ojalá pudiera acepta dos respuestas! Gracias!
    • Siéntase libre de aceptar a este si que se adapte mejor!
    • Es lo que hace. Pero creo que la solución se adapte a otras personas con la misma pregunta mejor.
    • Hay alguna manera explícita elegir una sobrecarga compililation tiempo? Como print<Derived, Base>(derived)
    • debe seleccionar la sobrecarga de Base. Si, es totalmente horrible.
    • Jaja gracias Casey!
    • Hey Casey, es también posible si la base es una clase de plantilla, mientras que no importa que los parámetros de la toma? por ejemplo, Iteratable<DoesNotMatterType>
    • Esto ya no funciona con GCC 6.3 en ideone – da error: redefinition of ‘template<class T, class> void print(const T&)’
    • Yo no he dicho que era – sólo estoy señalar que su ideone ya no compila
    • Aha, gracias! No me di cuenta de que el link se fuera de sincronización con la respuesta. Debe ser fijo ahora.
    • Pregunta. Hace el segundo nombre de parámetro (la más larga) hablar/especifica las condiciones con respecto a la primera (clase T), o sobre el segundo?
    • La segunda/largo parámetros de la plantilla de aplicar restricciones a la primera T parámetros. Sólo son válidos cuando se T tiene la propiedad, y en caso contrario, la causa de sustitución de fracaso la eliminación de la carga correspondiente de la resolución de sobrecarga.
    • Por supuesto que todo tiene que ser válida, de lo contrario, causando SFINAE. Pero yo estoy haciendo esto porque quiero estar seguro de que lo que han escrito para la segunda plantilla de tipo de parámetro es acerca de otro tipo de parámetro y no el primero (class T). A la derecha? O es el 2do parámetro tipo de typename std::enable_if<std::is_base_of<Serializable, T>::value, Serializable>::type* = nullptr> la especificación de las condiciones necesarias para que el primer tipo de parámetro (es decir. si T es la base de la clase Serializable..). En otras palabras, estamos hablando de 2 diferentes parámetros de tipo aquí o uno? Gracias por responder.
    • Sólo el primer parámetro de plantilla T es un parámetro de tipo. El segundo parámetro de plantilla en ambos casos es un no-tipo de parámetro de tipo puntero a void cuando el enable_if se satisface la condición – y puntero-a-sustitución-el fracaso de otra manera -, cuyo valor por defecto nullptr.
    • A la derecha. Lo siento, perdido en la terminología. Entonces, ¿qué hace el segundo parámetro hacer en tu caso? Por qué es necesaria? Yo no veo que se utiliza en cualquier parte de la función. Es sólo mediante T.
    • El único propósito de los sin nombre segundo parámetro es la causa de la sustitución de error cuando T no cumple con los requisitos para la sobrecarga, la eliminación eficaz de que la sobrecarga de la sobrecarga de establecer en esa circunstancia. La intención aquí es que tenemos dos idénticos sobrecargas de print one of which will be selected by overload resolution when the type of the function parameter derives from Imprimible, and another which will be selected the the type of the function parameter derives from Serializable`.

  3. 2

    Considere esto:

    void print(const Printable& value) {
        cout << value << endl;
    }
    
    void print(const Serializable& value) {
        cout << value << endl;
    }

    Naturalmente, usted tendrá la correspondiente operator<< llamar a una función virtual en el lado derecho operando, lo que haría la impresión real.

Kommentieren Sie den Artikel

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

Pruebas en línea