Qualcuno può spiegare questo codice modello che mi dà le dimensioni di un array? [duplicare]
Domanda
Questa domanda ha già una risposta qui:
- Argomenti magici nei modelli di funzione 3 risposte
template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
return n;
}
La parte che non capisco sono i parametri per questa funzione modello. Cosa succede con l'array quando lo passo attraverso che fornisce n
come numero di elementi nell'array?
Soluzione
Bene, prima devi capire che provare a ottenere un valore da un array può darti un puntatore al suo primo elemento:
int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost
I riferimenti si riferiscono ad oggetti usando il loro tipo esatto o il loro tipo di classe base. La chiave è che il modello accetta le matrici per riferimento. Matrici (non riferimenti ad esse) in quanto i parametri non esistono in C ++. Se si assegna a un parametro un tipo di array, sarà invece un puntatore. Quindi è necessario utilizzare un riferimento quando vogliamo conoscere la dimensione dell'array passato. Le dimensioni e il tipo di elemento vengono automaticamente dedotti, come generalmente accade per i modelli di funzione. Il seguente modello
template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
return n;
}
Chiamato con il nostro array a
precedentemente definito avvierà implicitamente la seguente funzione:
size_t array_size(const int (&)[3]) {
return 3;
}
Che può essere usato in questo modo:
size_t size_of_a = array_size(a);
C'è una variazione che ho inventato qualche tempo fa [Modifica: risulta che qualcuno aveva già avuto la stessa idea qui ] che può determinare un valore al momento della compilazione. Invece di restituire direttamente il valore, assegna al modello un tipo di ritorno a seconda di n
:
template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];
Dici che se l'array ha n
, il tipo restituito è un riferimento ad un array con dimensioni n
e tipo di elemento char
. Ora puoi ottenere una dimensione determinata in fase di compilazione dell'array passato:
size_t size_of_a = sizeof(array_size(a));
Poiché un array di char
con elementi n
ha dimensioni di n
, ciò fornirà anche il numero di elementi in un determinato array. Al momento della compilazione, quindi puoi farlo
int havingSameSize[sizeof(array_size(a))];
Poiché la funzione non viene mai effettivamente chiamata, non è necessario definirla, quindi non ha un corpo. Spero di poter chiarire un po 'la questione.
Altri suggerimenti
Pensaci in questo modo, supponi di avere un sacco di funzioni:
// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
return 1;
}
size_t array_size(const int (&)[2])
{
return 2;
}
size_t array_size(const int (&)[3])
{
return 3;
}
// etc...
Ora quando lo chiami, quale funzione viene chiamata?
int a[2];
array_size(a);
Ora se modifichi la matrice, ottieni:
template <int n>
size_t array_size(const int (&)[n])
{
return n;
}
Il compilatore tenterà di creare un'istanza di una versione di array_size che corrisponde a qualunque parametro lo chiami. Quindi, se lo chiami con un array di 10 pollici, verrà istanziato array_size con n = 10.
Quindi, templatizza semplicemente il tipo, così puoi chiamarlo con più di semplici array int:
template <typename T, int n>
size_t array_size(const T (&)[n])
{
return n;
}
E il gioco è fatto.
Modifica : una nota sul (&)
Le parentesi sono necessarie intorno al & amp;
per distinguere tra array di riferimenti int (illegali) e riferimenti a array di ints (ciò che vuoi). Poiché la precedenza di []
è superiore a & amp;
, se hai la dichiarazione:
const int &a[1];
a causa della precedenza dell'operatore, si finisce con un array di un elemento di riferimenti const a int. Se vuoi prima applicare & amp;
, devi forzarlo con le parentesi:
const int (&a)[1];
Ora hai un riferimento const a una matrice di in un elemento. Nell'elenco dei parametri delle funzioni, non è necessario specificare il nome di un parametro se non lo si utilizza, quindi è possibile rilasciare il nome, ma mantenere le parentesi:
size_t array_size(const int (&)[1])
All'array non succede nulla. È un parametro inutilizzato utilizzato per risolvere la firma della funzione modello.
Inoltre non può essere usato come argomento template, ma è un nit separato.
Un modo un po 'strano per ottenere il risultato come const in fase di compilazione per quelli di noi che non hanno " constexpr " ;:
#include <iostream>
namespace
{
template <size_t V>
struct helper
{
enum
{
value = V
};
};
template<typename T, size_t Size>
auto get_size(T(&)[Size]) -> helper < Size >
{
return helper < Size >() ;
}
template<typename T>
struct get_value
{
enum
{
value = T::value
};
};
}
int main()
{
std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}