2012-01-25 14 views
5

Estoy usando el siguiente 'truco' en tiempo de compilación (basado en ADL) para crear una función que sea válida/definida/invocable solo por clases en el mismo espacio de nombres.Especializando una plantilla de método para clases en un espacio de nombres

namespace Family1 
    { 
     struct ModelA{}; 
     struct ModelB{}; 

     template<typename T> 
     bool is_in_Family1(T const& t) 
     { 
      return true; 
     } 
    }; 

    namespace Family2 
    { 
     struct ModelC{}; 

     template<typename T> 
     bool is_in_Family2(T const& t) 
     { 
      return true; 
     } 
    }; 


    Family1::ModelA mA; 
    Family2::ModelC mC; 

    is_in_Family1(mA);   // VALID 
    is_in_Family1(mC);   // ERROR 

Ahora, me gustaría utilizar este principio (o algo similar) con el fin de producir una especialización de Foo::Bar (abajo) para las clases que pertenecen a cada uno de los espacios de nombres, por ejemplo, Family1.

// I would like to specialize the method template Bar for classes in Family1 
    // namespace; and another specialization for classes in Family2 namespace 
    struct Foo 
    { 
     template<typename T> 
     void Bar(T& _T){} 
    }; 

Para facilitar el mantenimiento y el gran número de clases en cada espacio de nombres, si es posible, me gustaría realizar esta comprobación sin nombrar a todas las clases en un espacio de nombres.

+0

¿Por qué 'Foo' es una clase de plantilla cuyo constructor también es una plantilla con un tipo diferente? 'Foo myvar ('4');'? ¿Solo quisiste tener una 'plantilla '? Porque el exterior no se usa. Creo que esto podría hacerse si 'Foo' fuera solo una función, pero no creo que pueda ser para una estructura. O al menos, no muy bien. –

Respuesta

0

La forma más rápida que he encontrado para hacer esto es el uso de caracteres de tipo Boost is_base_of <>

He intentado utilizar inheritence con especialización de plantilla, pero que no funcionó porque la herencia se ignora cuando especialización de plantilla se utiliza para que Tendría que especializarse para cada modelo. La respuesta a Partial specialization for a parent of multiple classes explica el problema.

El uso de caracteres de tipo de obras, siempre que hagas Familia1 :: subclases ModelA y Familia :: ModelB de Familia1: Family1Type y Familia2 :: ModelC una subclase de Familia2 :: Family2Type:

#include <iostream> 
#include <boost/type_traits/is_base_of.hpp> 

namespace Family1{ 

    struct Family1Type{}; 

    struct ModelA :public Family1Type{}; 
    struct ModelB :public Family1Type{}; 

    template<typename T> 
    bool is_in_Family1(const T& t){ 
     return boost::is_base_of<Family1::Family1Type,T>::value; 
    } 
}; 

namespace Family2{ 
    struct Family2Type{}; 

    struct ModelC :public Family2Type{}; 

    template<typename T> 
    bool is_in_Family2(const T& t){ 
     return boost::is_base_of<Family2::Family2Type,T>::value; 
    } 

}; 

using namespace std; 
int main(int argc, char *argv[]) { 

    Family1::ModelA mA; 
    Family2::ModelC mC; 

    std::cout << "mA is in Family1? " << is_in_Family1(mA) << std::endl; 
    std::cout << "mC is in Family2? " << is_in_Family2(mC) << std::endl; 

    //std::cout << "mC is in Family1? " << is_in_Family1(mC) << std::endl; //ERROR! 
    //std::cout << "mA is in Family2? " << is_in_Family2(mA) << std::endl; //ERROR! 

    return 0; 
} 

Esto se traduce en lo siguiente salida:

mA is in Family1? 1 
mC is in Family2? 1 

no creo que hay una manera de declarar Foo y especializado Foo::Bar<> en otro espacio de nombres según la Specialization of 'template<class _Tp> struct std::less' in different namespace

+0

Gracias @Joel. ¿Qué hay de la especialización de 'Foo' para cada espacio de nombres? – Olumide

+0

Scratch that. Lo que realmente me gustaría hacer es especializar un método de 'Foo' para cada espacio de nombres. He editado mi publicación en consecuencia. – Olumide

+0

@Olumide Parece que no puedo entender cómo hacerlo en este momento sin declarar la estructura de Foo en cada espacio de nombres. Parece que no puedo definir Foo fuera de un espacio de nombres donde implemento una especialización de él. – Joel

1

Su "truco" tiene un gran problema. Intente llamar al is_in_Family1(make_pair(Family1::ModelA(), Family2::ModelC()) y verá esa devolución true, porque ADL buscará en los espacios de nombres de ModelA y ModelC (debido a pair<ModelA, ModelC>).

Ignorando ese problema, con el uso de sus funciones es sencillo.

template<typename T> struct int_ { typedef int type; }; 

struct Foo 
{ 
    template<typename T, 
      typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0 
    > 
    void Bar(T& t){} 

    template<typename T, 
      typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0 
    > 
    void Bar(T& t){} 
}; 

que llama Bar dependiendo de si se está en familia2 o familia1.

struct Foo 
{ 
    template<typename T, 
      typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0 
    > 
    void Bar(T& t, long){} 

    template<typename T, 
      typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0 
    > 
    void Bar(T& t, long){} 

    template<typename T> 
    void Bar(T& t, int) {} 

    template<typename T> 
    void Bar(T& t) { return Bar(t, 0); } 
}; 

Ese tiene también un respaldo genérico. Y su código tenía un comportamiento indefinido porque utilizó un nombre reservado. No use _T.

+0

Gracias @litb! (Actualmente estoy estudiando tu respuesta.) ¿Hay una alternativa para que 'is_in_Family' devuelva un booleano? Por otra parte, ¿por qué un par es un argumento válido para 'is_in_Family'? – Olumide

+0

@olu dunno about alrernatives. es un argumento válido porque lo convirtió en una plantilla para que acepte cualquier cosa. –

Cuestiones relacionadas