Qualcuno può spiegare questo codice modello che mi dà le dimensioni di un array? [duplicare]

StackOverflow https://stackoverflow.com/questions/437150

  •  22-07-2019
  •  | 
  •  

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?

È stato utile?

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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top