c++ - Especialización de plantillas de miembros particulares?
templates specialization (2)
¿Es posible especializar a miembros particulares de una clase de plantilla? Algo como:
template <typename T,bool B>
struct X
{
void Specialized();
};
template <typename T>
void X<T,true>::Specialized()
{
...
}
template <typename T>
void X<T,false>::Specialized()
{
...
}
Por supuesto, este código no es válido.
Esto es lo que se me ocurrió, no tan mal :)
//The generic template is by default ''flag == false''
template <class Type, bool flag>
struct something
{
void doSomething()
{
std::cout << "something. flag == false";
}
};
template <class Type>
struct something<Type, true> : public something<Type, false>
{
void doSomething() // override original dosomething!
{
std::cout << "something. flag == true";
}
};
int main()
{
something<int, false> falseSomething;
something<int, true> trueSomething;
falseSomething.doSomething();
trueSomething.doSomething();
}
Solo puede especializarlo de forma explícita proporcionando todos los argumentos de la plantilla. No se permite ninguna especialización parcial para las funciones miembro de las plantillas de clase.
template <typename T,bool B>
struct X
{
void Specialized();
};
// works
template <>
void X<int,true>::Specialized()
{
...
}
Una solución alternativa es la introducción de funciones sobrecargadas, que tienen la ventaja de seguir estando en la misma clase, y por lo tanto tienen el mismo acceso a las variables miembro, funciones y productos.
// "maps" a bool value to a struct type
template<bool B> struct i2t { };
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl(i2t<B>()); }
private:
void SpecializedImpl(i2t<true>) {
// ...
}
void SpecializedImpl(i2t<false>) {
// ...
}
};
Tenga en cuenta que al pasar a las funciones sobrecargadas y al insertar los parámetros de la plantilla en un parámetro de función, puede arbitrariamente "especializar" sus funciones, y también puede adaptarlas según sea necesario. Otra técnica común es diferir a una plantilla de clase definida por separado
template<typename T, bool B>
struct SpecializedImpl;
template<typename T>
struct SpecializedImpl<T, true> {
static void call() {
// ...
}
};
template<typename T>
struct SpecializedImpl<T, false> {
static void call() {
// ...
}
};
template <typename T,bool B>
struct X
{
void Specialized() { SpecializedImpl<T, B>::call(); }
};
Encuentro que usualmente requiere más código y encuentro que la sobrecarga de la función es más fácil de manejar, mientras que otros prefieren el modo diferir a la plantilla de la clase. Al final es una cuestión de gusto. En este caso, podría haber puesto esa otra plantilla dentro de X
también como una plantilla anidada; en otros casos en los que se especializa explícitamente en lugar de solo parcialmente, no puede hacerlo, porque puede colocar especializaciones explícitas solo en el ámbito del espacio de nombres. no en el alcance de la clase.
También podría crear una plantilla de SpecializedImpl
con el propósito de sobrecargar la función (luego funciona de manera similar a nuestro i2t
de antes), como lo demuestra la siguiente variante, que también deja la primera variable de parámetro (para que pueda llamarla con otros tipos, no solo con los parámetros de plantilla de la instanciación actual)
template <typename T,bool B>
struct X
{
private:
// maps a type and non-type parameter to a struct type
template<typename T, bool B>
struct SpecializedImpl { };
public:
void Specialized() { Specialized(SpecializedImpl<T, B>()); }
private:
template<typename U>
void Specialized(SpecializedImpl<U, true>) {
// ...
}
template<typename U>
void Specialized(SpecializedImpl<U, false>) {
// ...
}
};
Creo que a veces, diferir a otra plantilla es mejor (cuando se trata de arreglos y punteros, la sobrecarga puede ser complicada y el reenvío a una plantilla de clase ha sido más fácil para mí), y algunas veces es mejor sobrecargar la plantilla: especialmente si realmente reenvía argumentos de funciones y si toca las variables de miembros de las clases.