2009-01-31 9 views
85

Cuando se declara una plantilla, estoy acostumbrado a tener este tipo de código:¿Qué significa la plantilla <unsigned int N>?

template <class T> 

Pero in this question, utilizaron:

template <unsigned int N> 

Comprobé que compila. Pero, ¿qué significa? ¿Es un parámetro sin tipo? Y si es así, ¿cómo podemos tener una plantilla sin ningún tipo de parámetro?

Respuesta

116

Es perfectamente posible moldear una clase en un entero en lugar de un tipo. Podemos asignar el valor de plantilla a una variable, o manipular de una manera que puede ser que con cualquier otro entero literal:

unsigned int x = N; 

De hecho, podemos crear algoritmos que evalúan en tiempo de compilación (desde Wikipedia):

template <int N> 
struct Factorial 
{ 
    enum { value = N * Factorial<N - 1>::value }; 
}; 

template <> 
struct Factorial<0> 
{ 
    enum { value = 1 }; 
}; 

// Factorial<4>::value == 24 
// Factorial<0>::value == 1 
void foo() 
{ 
    int x = Factorial<4>::value; // == 24 
    int y = Factorial<0>::value; // == 1 
} 
+1

También puede usar el tipo 'static constexpr int' en lugar de su' enum'. Entonces, la plantilla 'Factorial <0>' tendría 'static constexpr int value = 1', y' template struct Factorial' puede tener 'static constexpr int value = N * Factorial :: value;' – bobobobo

14

Templalate su clase basándose en un 'unsigned int'.

Ejemplo:

template <unsigned int N> 
class MyArray 
{ 
    public: 
    private: 
     double data[N]; // Use N as the size of the array 
}; 

int main() 
{ 
    MyArray<2>  a1; 
    MyArray<2>  a2; 

    MyArray<4>  b1; 

    a1 = a2; // OK The arrays are the same size. 
    a1 = b1; // FAIL because the size of the array is part of the 
       //  template and thus the type, a1 and b1 are different types. 
       //  Thus this is a COMPILE time failure. 
} 
128

Sí, es un parámetro no tipo. Puede tener varios tipos de parámetros de plantilla

  • parámetros de tipo.
    • Tipos
    • plantillas (sólo clases y plantillas alias, no hay funciones o plantillas variables)
  • de tipo no Parámetros
    • Punteros
    • Referencias
    • expresiones constantes Integral

Lo que tienes allí es del último tipo. Es una constante de tiempo de compilación (llamada expresión constante) y es de tipo entero o enumeración. Después de buscarlo en el estándar, tuve que mover las plantillas de clases a la sección de tipos, aunque las plantillas no son tipos. Pero se llaman parámetros de tipo con el fin de describir esos tipos, no obstante. Puede tener punteros (y también punteros de miembro) y referencias a objetos/funciones que tienen enlaces externos (aquellos a los que se puede vincular desde otros archivos de objetos y cuya dirección es única en todo el programa).Ejemplos:

plantilla de parámetros tipo:

template<typename T> 
struct Container { 
    T t; 
}; 

// pass type "long" as argument. 
Container<long> test; 

Plantilla parámetro entero:

template<unsigned int S> 
struct Vector { 
    unsigned char bytes[S]; 
}; 

// pass 3 as argument. 
Vector<3> test; 

parámetro de puntero de plantilla (que pasa un puntero a una función) parámetro de referencia Plantilla

template<void (*F)()> 
struct FunctionWrapper { 
    static void call_it() { F(); } 
}; 

// pass address of function do_it as argument. 
void do_it() { } 
FunctionWrapper<&do_it> test; 

(pasando un número entero)

template<int &A> 
struct SillyExample { 
    static void do_it() { A = 10; } 
}; 

// pass flag as argument 
int flag; 
SillyExample<flag> test; 

Parámetro de plantilla de plantilla.

template<template<typename T> class AllocatePolicy> 
struct Pool { 
    void allocate(size_t n) { 
     int *p = AllocatePolicy<int>::allocate(n); 
    } 
}; 

// pass the template "allocator" as argument. 
template<typename T> 
struct allocator { static T * allocate(size_t n) { return 0; } }; 
Pool<allocator> test; 

Una plantilla sin ningún parámetro no es posible. Pero una plantilla sin ningún argumento explícito es posible - que tiene los argumentos por defecto:

template<unsigned int SIZE = 3> 
struct Vector { 
    unsigned char buffer[SIZE]; 
}; 

Vector<> test; 

sintácticamente, template<> está reservado para marcar una especialización de plantilla explícita, en lugar de una plantilla sin parámetros:

template<> 
struct Vector<3> { 
    // alternative definition for SIZE == 3 
}; 
+0

Johannes, son plantillas archivadas en " tipos "? Pensé que eran de qué tipos se pueden hacer, pero no tipos ellos mismos? – sbi

+0

@sbi ver la explicación: "Después de mirar hacia arriba en la norma, lo que tenía que mover las plantillas de clase en la sección de tipos - a pesar de que las plantillas no son tipos Pero se les llama de tipo parámetros para el propósito de describir ese tipo, no obstante. . ". La nota al pie 126 en 14.1/2 lo dice. Es solo una clasificación hecha para hacer que los parámetros que no sean del tipo sean algo que declare un valor/referencia y los parámetros tipo sean algo que declare un nombre de tipo o un nombre de plantilla. –

+0

@ JohannesSchaub-litb entonces no hay forma de tipear la plantilla con let std :: string? como la plantilla class con algún contador estático para crear una identificación única para cada cadena diferente? hashing string to int sería la única forma lamentablemente correcta? – relaxxx

12

Una plantilla la clase es como una macro, solo mucho menos malvada.

Piense en una plantilla como una macro. Los parámetros de la plantilla se sustituyen en una definición de clase (o función) cuando define una clase (o función) utilizando una plantilla.

La diferencia es que los parámetros tienen "tipos" y los valores pasados ​​se controlan durante la compilación, como los parámetros de las funciones. Los tipos válidos son sus tipos normales de C++, como int y char. Cuando crea una instancia de una clase de plantilla, pasa un valor del tipo que ha especificado, y en una nueva copia de la definición de la clase de la plantilla, este valor se sustituye en cualquier lugar donde el nombre del parámetro estaba en la definición original. Como una macro.

También puede usar los tipos "class" o "typename" para los parámetros (son realmente los mismos). Con un parámetro de uno de estos tipos, puede pasar un nombre de tipo en lugar de un valor. Al igual que antes, en todas partes donde el nombre del parámetro estaba en la definición de la clase de la plantilla, tan pronto como crea una nueva instancia, se convierte en el tipo que pase. Este es el uso más común para una clase de plantilla; Todo el mundo que sabe algo sobre plantillas C++ sabe cómo hacerlo.

Considere este código de ejemplo de la clase de plantilla:

#include <cstdio> 
template <int I> 
class foo 
{ 
    void print() 
    { 
    printf("%i", I); 
    } 
}; 

int main() 
{ 
    foo<26> f; 
    f.print(); 
    return 0; 
}

Es funcionalmente lo mismo que esta macro utilizando el código:

#include <cstdio> 
#define MAKE_A_FOO(I) class foo_##I \ 
{ \ 
    void print() \ 
    { \ 
    printf("%i", I); \ 
    } \ 
}; 

MAKE_A_FOO(26) 

int main() 
{ 
    foo_26 f; 
    f.print(); 
    return 0; 
}

Por supuesto, la versión de la plantilla es de mil millones de veces más seguro y más flexible .