2011-01-30 9 views
28

¿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? Estoy intentando crear constructores especiales para sólo ciertos tipos de combinaciones de plantilla:Especialización en plantillas C++ con valor constante

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á y devolverá un error Expected identifier before numeric constant en la segunda definición.

He echado un vistazo a través de una serie de 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 manera de escribir un constructor utilizado de forma condicional, algo funcionalmente como esto:

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]; 
}; 
+0

Chceck my answer. Parece hacer exactamente lo que quieres. –

Respuesta

7

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, sólo debes copiar y pegar de 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.

+0

Usando ese enfoque me sale un error 'Especialización explícita en el ámbito de espacio de nombres no- – tadman

+0

Supongo que eso es porque estoy usando VS .. http: // stackoverflow. com/questions/3052579/explicit-specialization-in-non-namespace-scope – Marlon

4

Si falla la memoria, que 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; }; 
}; 

No creo que esto esté permitido como está, sin embargo, 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 hacen no comparten cualquiera de la 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.

+0

Tiene que especificar dos argumentos de plantilla –

+0

Sí, estoy bastante afectado con diferentes opciones, pero aparece con errores, declara una clase no relacionada o una subclase que no aumenta bastante la clase principal correctamente. Los tipos de argumentos perdidos son mi trabajo de cortar y pegar mal. – tadman

18

Es necesario poner la especialización en el lugar correcto:

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

Si desea crear una subclase:

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

El comportamiento de especializada en typedefs es similar al comportamiento de especializada en un parámetro entero.

+0

Esto parece crear una clase completamente no relacionada donde 'value' no está definido. – tadman

+0

Puede escribir la plantilla class EjemploEspecialización: public Ejemplo - answer updated –

+0

Supongo que me he chocado contra una pared. La clase principal introducirá instancias de sí mismo cuando se invoquen métodos genéricos, pero las subclases emitirán subclases incompatibles. Supongo que no hay forma de escribir un constructor condicional y/o anular métodos específicos en casos específicos. – tadman

2

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 Example para que no tenga que volver a escribirla en cada especialización que se te ocurre

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 
    }; 
2

Usted puede intentar algo como esto: ejemplo

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]; 
}; 

Trabajando en http://www.ideone.com/wDcL7

En plantillas si no está utilizando el método que no existe en 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).

+0

Ese es un enfoque interesante, pero el truco que estaba buscando era declarar los parámetros de clase completa más plantilla en las definiciones de constructor. – tadman

+0

¿Y cómo es diferente de agregar 'STATIC_ASSERT' a dicho constructor? No puede usarlo en el código, por lo que el código no existe. –

0
#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(); 
} 

que 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. Este es un software libre; ver la fuente de las condiciones de copia. No hay garantía de ; ni siquiera para COMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR.

Cuestiones relacionadas