2009-12-08 12 views
6

perche questo codice:Array decadimento di puntatori in modelli

#include <iostream> 

template<typename T> 
void f(T x) { 
    std::cout << sizeof(T) << '\n'; 
} 

int main() 
{ 
    int array[27]; 
    f(array); 
    f<decltype(array)>(array); 
} 

Nota del redattore: il codice originale usato typeof(array), tuttavia, che è un'estensione GCC.

Ciò stamperà

8 (or 4) 
108 

Nel primo caso, la matrice decade ovviamente un puntatore e T diventa int*. Nel secondo caso, T è forzato a int[27]. L'ordine di decadimento/implementazione di sostituzione è definito? C'è un modo più elegante per forzare il tipo a int[27]? Oltre a usare std :: vector?

+3

Dove hai trovato un compilatore C++ dove 'sizeof (int) == 1'? Ottengo 108 per la seconda chiamata. –

+0

Sì, ovviamente gcc ha già spostato fino a 4 byte interi ;-) Ho introdotto un bug durante la generazione di un testcase. – hirschhornsalz

+0

Sono sorpreso della compilazione della seconda chiamata. Non è possibile passare array per valore in C++. [edit: Ah, 'T' ha il tipo di matrice, ma' sizeof (x) 'produrrebbe ancora 8 o 4. Non importa. :)] –

risposta

9

usare il tipo di riferimento per il parametro

template<typename T> void f(const T& x) 
{ 
    std::cout << sizeof(T); 
} 

nel qual caso il tipo di matrice non decadrà.

Allo stesso modo, è inoltre possibile impedire il decadimento nella versione originale di f se si specifica esplicitamente il modello agument T come un tipo di riferimento a-array

f<int (&)[27]>(array); 

Nel tuo esempio di codice originale, costringendo l'argomento T avere il tipo di matrice (cioè il tipo di matrice non di riferimento, usando typeof o specificando esplicitamente il tipo), non impedirà il decadimento del tipo di array. Mentre lo stesso T corrisponde al tipo di array (come osservato), il parametro x verrà comunque dichiarato come puntatore e sizeof x valuterà comunque la dimensione del puntatore.

+0

Sì, ma sto usando sizeof (T). Comunque, grazie per la risposta veloce :-) – hirschhornsalz

1

A seconda del caso d'uso, è possibile aggirare che using references:

template<typename T> 
void f(const T& x) { 
    std::cout << sizeof(T); 
} 

char a[27]; 
f(a); 

che stampa 27, se lo desideri.

+0

Probabilmente dovresti usare un riferimento costante, dato che non stai modificando T. – GManNickG

+0

Giusto, ho appena fatto le modifiche minime necessarie al codice OPs. –

1

È inoltre possibile utilizzare i modelli come la seguente:

template <typename T, std::size_t N> 
inline std::size_t number_of_elements(T (&ary)[N]) { 
    return N; 
} 

Questo piccolo trucco causerà errori di compilazione se la funzione viene utilizzata su un tipo non-array.

2

Il comportamento di questo codice è spiegato da C++ 14 [temp.deduct.chiamare]:

Dedurre argomenti di template da una chiamata di funzione

argomento Template deduzione è fatto confrontando ogni funzione tipo di parametro template (lo chiamano P) con il tipo del corrispondente argomento della chiamata (chiamare A) come descritto di seguito

e poi sotto:

Se P non è un tipo di riferimento:

  • Se A è un tipo di matrice, il tipo di puntatore prodotto dalla conversione da matrice a puntatore standard (4.2) è utilizzato al posto di A per tipo detrazione;

Per la chiamata f(array);, abbiamo A = int[27]. A è un tipo di matrice. Quindi il tipo dedotto T è int *, in base a quest'ultimo punto.

Possiamo vedere dal qualificatore "Se P non è un tipo di riferimento" che questo comportamento potrebbe forse essere evitato rendendo P un tipo di riferimento. Per il codice:

template<typename T, size_t N> 
void f(T (&x)[N]) 

simbolo P mezzi T(&)[N], che è un tipo di riferimento; e si scopre che non ci sono conversioni applicate qui. T viene dedotto a int, con il tipo di xint(&)[N].


Si noti che questo vale solo per i modelli di funzione dove il tipo si deduce dalla discussione. Il comportamento è coperto da parti separate della specifica per i parametri del modello di funzione esplicitamente forniti e dai modelli di classe.

Problemi correlati