Come funziona questo codice modello per ottenere le dimensioni di un array?


61

Mi chiedo perché questo tipo di codice possa ottenere le dimensioni dell'array di test? Non ho familiarità con la grammatica nel modello. Forse qualcuno potrebbe spiegare il significato del codice sotto template<typename,size_t>. Inoltre, è preferito anche un collegamento di riferimento.

#define dimof(array) (sizeof(DimofSizeHelper(array)))
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

void InitDynCalls()
{
    char test[20];
    size_t n = dimof(test);
    printf("%d", n);
}


Hai letto qualcosa come n3337 su C ++ 11 ? Dovrebbe essere pertinente alla tua domanda! Hai pensato di utilizzare std::arrayo std::vector...
Basile Starynkevitch il

@BasileStarynkevitch Non l'ho letto. Il codice appare in una libreria di terze parti. Voglio solo capire il significato.
Demone dell'Ombra

Vedi anche norvig.com/21-days.html per una visione utile (e guarda chi è l'autore di quella pagina).
Basile Starynkevitch,

2
Si presenta come un duplicato di stackoverflow.com/questions/6106158/...
Sharptooth

@BasileStarynkevitch Non capisco la rilevanza di quel link.
Razze di leggerezza in orbita

Risposte:


86

Questo in realtà è molto difficile da spiegare, ma ci proverò ...

Innanzitutto, dimofindica la dimensione o il numero di elementi in un array. (Credo che "dimensione" sia la terminologia preferita negli ambienti di programmazione di Windows).

Questo è necessario perché C++e Cnon ti dà un modo nativo per determinare la dimensione di un array.


Spesso le persone credono sizeof(myArray)che funzionerà, ma che in realtà ti darà la dimensione in memoria, piuttosto che il numero di elementi. Ogni elemento probabilmente richiede più di 1 byte di memoria!

Successivamente, potrebbero provare sizeof(myArray) / sizeof(myArray[0]). Ciò darebbe la dimensione in memoria dell'array, divisa per la dimensione del primo elemento. È ok e ampiamente usato nel Ccodice. Il problema principale con questo è che sembrerà funzionare se si passa un puntatore anziché un array. La dimensione di un puntatore in memoria sarà generalmente di 4 o 8 byte, anche se la cosa a cui punta potrebbe essere una matrice di migliaia di elementi.


Quindi la prossima cosa da provare C++è usare i template per forzare qualcosa che funziona solo per gli array, e darà un errore del compilatore su un puntatore. Sembra così:

template <typename T, std::size_t N>
std::size_t ArraySize(T (&inputArray)[N])
{
    return N;
}
//...
float x[7];
cout << ArraySize(x); // prints "7"

Il modello funzionerà solo con un array. Ne dedurrà il tipo (non proprio necessario, ma deve essere lì per far funzionare il modello) e la dimensione dell'array, quindi restituisce la dimensione. Il modo in cui il modello è scritto non può funzionare con un puntatore.

Di solito puoi fermarti qui, e questo è nella C ++ Standard Libary come std::size.


Attenzione: qui sotto entra nel territorio peloso di avvocato linguistico.


Questo è piuttosto interessante, ma fallisce comunque in un caso oscuro:

struct Placeholder {
    static float x[8];
};

template <typename T, int N>
int ArraySize (T (&)[N])
{
    return N;
}

int main()
{
    return ArraySize(Placeholder::x);
}

Si noti che l'array xè dichiarato , ma non definito . Per chiamare una funzione (cioè ArraySize) con essa, xdeve essere definito .

In function `main':
SO.cpp:(.text+0x5): undefined reference to `Placeholder::x'
collect2: error: ld returned 1 exit status

Non puoi collegarlo.


Il codice che hai nella domanda è un modo per aggirare quello. Invece di chiamare effettivamente una funzione, dichiariamo una funzione che restituisce un oggetto della giusta dimensione . Quindi usiamo il sizeoftrucco su questo.

E sembra come noi chiamiamo la funzione, ma sizeofè puramente un costrutto fase di compilazione, quindi la funzione non viene effettivamente chiamato.

template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];
^^^^ ^                               ^^^
// a function that returns a reference to array of N chars - the size of this array in memory will be exactly N bytes

Nota che non puoi effettivamente restituire un array da una funzione, ma puoi restituire un riferimento a un array.

Quindi DimofSizeHelper(myArray)è un'espressione il cui tipo è un array su N chars. In realtà l'espressione non deve essere eseguibile, ma ha senso al momento della compilazione.

Pertanto sizeof(DimofSizeHelper(myArray))ti dirà la dimensione in fase di compilazione di ciò che otterresti se in realtà chiamassi la funzione. Anche se in realtà non lo chiamiamo.

Austin Powers strabico


Non preoccuparti se l'ultimo blocco non avesse alcun senso. È un trucco bizzarro aggirare un bizzarro caso limite. Questo è il motivo per cui non scrivi tu stesso questo tipo di codice e lasci che gli implementatori di librerie si preoccupino di questo tipo di sciocchezze.


3
@Shadowfiend È anche sbagliato. Le cose sono ancora più brutte di così, perché in realtà non è una dichiarazione di una funzione, è una dichiarazione di riferimento di una funzione ... Sto ancora cercando di capire come spiegarlo.
BoBTFish

5
Perché è una dichiarazione di un riferimento di funzione? Il "&" prima di "DimofSizeHelper" indica che il tipo restituito è char (&) [N], in accordo con la risposta di bolov.
Demone dell'Ombra

3
@Shadowfiend Assolutamente giusto. Stavo solo parlando di immondizia perché mi sono fatto un nodo al cervello.
BoBTFish

La dimensione non è il numero di elementi in un array. Cioè, potresti avere matrici di dimensioni 1, 2, 3 o superiori, ognuna con lo stesso numero di elementi. Ad esempio array1D [1000], array 2D [10] [100], array3D [10] [10] [10]. ciascuno con 1000 elementi.
jamesqf,

1
@jamesqf In linguaggi come C ++, un array multidimensionale è semplicemente un array che contiene altri array. Dal punto di vista del compilatore, il numero di elementi nell'array primario è spesso completamente estraneo al suo contenuto, che può essere una matrice secondaria o terziaria.
Phlarx,

27
template <typename T, size_t N>
char(&DimofSizeHelper(T(&array)[N]))[N];

// see it like this:
//                char(&DimofSizeHelper(T(&array)[N]))[N];
// template name:       DimofSizeHelper
// param name:                             array
// param type:                          T(&     )[N])
// return type:   char(&                             )[N];

DimofSizeHelperè una funzione modello che accetta un T(&)[N]parametro, ovvero un riferimento a un array C di N elementi di tipo Te restituisce un char (&)[N]aka un riferimento a un array di N caratteri. In C ++ un char è un byte sotto mentite spoglie ed sizeof(char)è garantito 1dallo standard.

size_t n = dimof(test);
// macro expansion:
size_t n = sizeof(DimofSizeHelper(array));

nviene assegnata la dimensione del tipo restituito di DimofSizeHelper, che è sizeof(char[N])quale N.


Questo è un po 'contorto e non necessario. Il solito modo di farlo era:

template <class T, size_t N>
/*constexpr*/ size_t sizeof_array(T (&)[N]) { return N; }

Dal C ++ 17 anche questo non è necessario, come abbiamo fatto std::size, ma in modo più generico, essere in grado di ottenere le dimensioni di qualsiasi contenitore in stile stl.


Come sottolineato da BoBTFish, è necessario per un caso limite.


2
È necessario se non è possibile utilizzare ODR nell'array di cui si desidera prendere le dimensioni (è dichiarato ma non definito). Certo, piuttosto oscuro.
BoBTFish

Grazie per aver spiegato il tipo nella funzione template. Questo aiuta davvero.
Demone dell'Ombra

3
Abbiamo std::extentdal C ++ 11 che è tempo di compilazione.
LF
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.