Cosa significa template <unsigned int N>?


121

Quando dichiaro un modello, sono abituato ad avere questo tipo di codice:

template <class T>

Ma in questa domanda , hanno usato:

template <unsigned int N>

Ho controllato che si compili. Ma cosa vuol dire? È un parametro non di tipo? E se è così, come possiamo avere un modello senza alcun parametro di tipo?

Risposte:


148

È perfettamente possibile modellare una classe su un numero intero anziché su un tipo. Possiamo assegnare il valore modellato a una variabile, o altrimenti manipolarlo in un modo che potremmo con qualsiasi altro valore letterale intero:

unsigned int x = N;

Possiamo infatti creare algoritmi che valutano in fase di compilazione (da 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
}

11
Puoi anche usare il tipo static constexpr intinvece del tuo enum. Quindi il Factorial<0>modello avrebbe static constexpr int value = 1e template <int N> struct Factorialpuò averestatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo

@bobobobo questa è stata risposta prima di C ++ 11 e constexpr.
Justin Meiners

154

Sì, è un parametro non di tipo. Puoi avere diversi tipi di parametri del modello

  • Parametri di tipo.
    • tipi
    • Modelli (solo modelli di classi e alias, nessuna funzione o modelli di variabili)
  • Parametri non di tipo
    • puntatori
    • Riferimenti
    • Espressioni costanti integrali

Quello che hai lì è dell'ultimo tipo. È una costante del tempo di compilazione (la cosiddetta espressione costante) ed è di tipo intero o enumerazione. Dopo averlo cercato nello standard, ho dovuto spostare i modelli di classe nella sezione dei tipi, anche se i modelli non sono tipi. Ma sono chiamati parametri di tipo allo scopo di descrivere comunque quei tipi. È possibile avere puntatori (e anche puntatori membri) e riferimenti a oggetti / funzioni che hanno un collegamento esterno (quelli che possono essere collegati da altri file oggetto e il cui indirizzo è univoco nell'intero programma). Esempi:

Parametro del tipo di modello:

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

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

Parametro intero modello:

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

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

Parametro del puntatore del modello (passaggio di un puntatore a una funzione)

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;

Parametro di riferimento del modello (passaggio di un numero intero)

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

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

Parametro del modello di modello.

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;

Un modello senza parametri non è possibile. Ma è possibile un modello senza alcun argomento esplicito: ha argomenti predefiniti:

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

Vector<> test;

Sintatticamente, template<>è riservato per contrassegnare una specializzazione di modello esplicita, invece di un modello senza parametri:

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

Johannes, i modelli sono archiviati in "tipi"? Ho pensato che fossero i tipi con cui si possono creare, ma non i tipi stessi?
sbi

@sbi vede la spiegazione: "Dopo aver cercato nello standard, ho dovuto spostare i modelli di classe in alto nella sezione dei tipi, anche se i modelli non sono tipi. Ma sono chiamati parametri di tipo allo scopo di descrivere quei tipi comunque. ". Lo dice la nota 126 del 14.1 / 2. È solo una classificazione fatta per rendere i parametri non di tipo qualcosa che dichiara un valore / riferimento e i parametri di tipo sono qualcosa che dichiara un nome di tipo o un nome di modello.
Johannes Schaub - litb

@ JohannesSchaub-litb quindi non c'è modo di digitare template con let say std :: string? come la classe template <std :: string S> con un contatore statico per creare un ID univoco per ogni stringa diversa? hashing string to int sarebbe l'unico modo purtroppo giusto?
relaxxx

1
Mi piacerebbe vedere questa risposta completata con oggetti del membro della classe modello, ad esempio template <typename C, typename R, typename P1, typename P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling

Il pezzo di codice con SillyExamplenon può essere compilato da GCC 4.8.4. Il primo errore è the value of ‘flag’ is not usable in a constant expression. Ci sono anche altri errori
HEKTO

17

Esegui il template della tua classe in base a un 'unsigned int'.

Esempio:

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.
 }

15

Una classe template è come una macro, solo molto meno malvagia.

Pensa a un modello come a una macro. I parametri del modello vengono sostituiti in una definizione di classe (o funzione), quando si definisce una classe (o funzione) utilizzando un modello.

La differenza è che i parametri hanno "tipi" e i valori passati vengono controllati durante la compilazione, come i parametri alle funzioni. I tipi validi sono i normali tipi C ++, come int e char. Quando si crea un'istanza di una classe modello, si passa un valore del tipo specificato e in una nuova copia della definizione della classe modello questo valore viene sostituito ovunque il nome del parametro fosse nella definizione originale. Proprio come una macro.

Puoi anche usare i tipi " class" o " typename" per i parametri (sono davvero gli stessi). Con un parametro di uno di questi tipi, puoi passare un nome di tipo invece di un valore. Proprio come prima, ovunque il nome del parametro fosse nella definizione della classe del modello, non appena crei una nuova istanza, diventa qualsiasi tipo tu passi. Questo è l'uso più comune per una classe template; Tutti quelli che sanno qualcosa sui modelli C ++ sanno come farlo.

Considera questo codice di esempio di classe modello:

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

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

Funzionalmente è lo stesso di questo codice che utilizza la macro:

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

Naturalmente, la versione del modello è un miliardo di volte più sicura e flessibile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.