c++ templates template-specialization

Especialización en plantillas C++ con valor constante



templates template-specialization (6)

¿Existe una manera directa de definir una especialización parcial de una clase de plantilla C ++ dada una constante numérica para uno de los parámetros de la plantilla? Intento crear constructores especiales solo para ciertos tipos de combinaciones de plantillas:

template <typename A, size_t B> class Example { public: Example() { }; A value[B]; }; template <typename A, 2> class Example { public: Example(b1, b2) { value[0] = b1; value[1] = b2; }; };

Este ejemplo no se compilará, devolviendo un error Expected identifier before numeric constant en la segunda definición.

He echado un vistazo a varios ejemplos aquí y en otros lugares, pero la mayoría parece girar en torno a la especialización con un tipo y no con una constante.

Editar:

Buscando una forma de escribir un constructor condicionalmente usado, algo funcionalmente como este:

template <typename A, size_t B> class Example { public: // Default constructor Example() { }; // Specialized constructor for two values Example<A,2>(A b1, A b2) { value[0] = b1; value[1] = b2; }; A foo() { A r; for (size_t i = 0; i < b; ++b) r += value[i]; return r; } // Hypothetical specialized implementation A foo<A, 2>() { return value[0] + value[1]; } A value[B]; };


Creo que esto podría funcionar:

#include <iostream> template <typename A, size_t B> class Example { public: Example() { Construct<B>(identity<A, B>()); } A foo() { return foo<B>(identity<A, B>()); } private: template <typename A, size_t B> struct identity {}; template <size_t B> void Construct(identity<A, B> id) { for (size_t i = 0; i < B; ++i) { value[i] = 0; } std::cout << "default constructor/n"; } template <size_t B> void Construct(identity<A, 2> id) { value[0] = 0; value[1] = 0; std::cout << "special constructor/n"; } template <size_t B> A foo(identity<A, B> id) { A r = 0; for (size_t i = 0; i < B; ++i) { r += value[i]; } std::cout << "default foo/n"; return r; } template <size_t B> A foo(identity<A, 2> id) { std::cout << "special foo/n"; return value[0] + value[1]; } A value[B]; }; int main() { Example<int, 2> example; // change the 2 to see the difference int n = example.foo(); std::cin.get(); return 0; }

Lo siento, solo copié y pegué desde mi proyecto de prueba. En realidad, no se trata de una "especialización", solo llama a las sobrecargas a funciones especializadas. No estoy seguro de si esto es lo que quieres y de que esto no es muy elegante.


Debes poner la especialización en el lugar correcto:

template <typename A> class Example<A,2>

Si quieres crear una subclase:

template <typename A> class ExampleSpecialization : public Example<A,2>

El comportamiento para especializarse en typedefs es similar al comportamiento para especializarse en un parámetro entero.


Puedes probar algo como esto:

template<size_t s> struct SizeTToType { static const size_t value = s; }; template<bool> struct StaticAssertStruct; template<> struct StaticAssertStruct<true> {}; #define STATIC_ASSERT(val, msg) { StaticAssertStruct<((val) != 0)> ERROR_##msg; (void)ERROR_##msg;} template <typename A, size_t B> class Example { public: Example() { }; Example(A b1){ value[0] = b1; } Example(A b1, A b2) { STATIC_ASSERT(B >= 2, B_must_me_ge_2); value[0] = b1; value[1] = b2; } A foo() { return in_foo(SizeTToType<B>()); } protected: template<size_t C> A in_foo(SizeTToType<C>) { cout << "univ" << endl; A r; for (size_t i = 0; i < B; ++i) r += value[i]; return r; } A in_foo(SizeTToType<2>){ cout << "spec" << endl; return value[0] + value[1]; } A value[B]; };

Ejemplo de trabajo en http://www.ideone.com/wDcL7

En las plantillas, si no está utilizando el método, no existirá en el código compilado, por lo que esta solución no debería hacer que el ejecutable sea más grande debido a los ctors que no puede usar con alguna clase especializada (por ejemplo, Example<int, 1> no debería tener Example(A b1, A b2) ctor).


Si la memoria sirve, debería ser más como:

template <typename A, size_t B> class Example { public: Example() { }; A value[B]; }; template <typename A> class Example<A, 2> { public: Example(A b1, A b2) { value[0] = b1; value[1] = b2; }; };

Sin embargo, no creo que esto esté permitido, ya que no hay nada que defina los tipos de b1 y / o b2 en la versión especializada.

Editar [basado en la pregunta editada]: Sí, una especialización de plantilla produce un nuevo tipo que no está realmente relacionado con la base desde la que se especializa. En particular, los dos no comparten ninguna implementación. No puede (al especializar una plantilla de clase) producir un solo tipo que utilice uno de dos ctors diferentes, según el valor de un parámetro que no sea de tipo.


Si su objetivo es solo tener que anular algunos métodos / constructores en sus especializaciones, entonces tal vez considere una clase base genérica para mantener la implementación común para todas las plantillas de Example para que no tenga que volver a escribirla en cada especialización que surja. con.

Por ejemplo:

template < typename A, size_t B > class ExampleGeneric { public: // generic implementation of foo inherited by all Example<A,B> classes void foo() { A r; for (size_t i = 0; i < B; ++i) r += value[i]; return r; } // generic implementation of bar inherited by all Example<A,B> classes void bar() { A r; for (size_t i = 0; i < B; ++i) r *= value[i]; return r; } A values[B]; }; template < typename A, size_t B > class Example : public ExampleGeneric<A,B> { public: //default to generic implementation in the general case by not overriding anything }; //*** specialization for 2 template < typename A > class Example<A,2> : public ExampleGeneric<A,2>{ public: // has to be provided if you still want default construction Example() { } //extra constructor for 2 parameters Example( A a1, A a2 ) { values[0] = a1; values[1] = a2; } // specialization of foo void foo() { return values[0] + values[1]; } // don''t override bar to keep generic version };


#include <iostream> using namespace std; template<typename _T, size_t S> class myclass { _T elem[S]; public: myclass() { for (int i = 0; i < S; i++) { elem[i] = i; } } void Print() { for (int i = 0; i < S; i++) { cout << "elem[" << i << "] = " << elem[i] << endl; } } }; int main(int argc, char **argv) { myclass < int, 10 > nums; nums.Print(); myclass < int, 22 > nums1; nums1.Print(); }

Eso funcionó en mi máquina linux con

g ++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) Copyright (C) 2006 Free Software Foundation, Inc. Esto es software libre; ver la fuente de las condiciones de copia. NO hay garantía; ni siquiera para COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR.