2014-09-08 18 views
120

Quali sono i motivi dell'esistenza di std::decay? In quali situazioni è utile std::decay?Che cos'è std :: decay e quando dovrebbe essere usato?

+3

Viene utilizzato nella libreria standard ad es. quando si passano argomenti a un thread. Questi devono essere * memorizzati *, in base al valore, quindi non è possibile memorizzare ad es. array. Invece, un puntatore viene memorizzato e così via. È anche una metafunzione che simula le regolazioni del tipo di parametro della funzione. – dyp

+3

'decay_t ' è una bella combinazione, per vedere cosa dedurrebbe 'auto'. –

+0

[meta.trans.other] afferma: Questo comportamento è simile al [...= Vari] conversioni applicata quando un'espressione lvalue è utilizzato come rvalue, ma anche strisce CV-qualificazioni da tipi di classe ** al fine modellare più strettamente per valore argomento passando **." – dyp

risposta

116

<joke> È ovviamente usato per decadere radioattivo std::atomic tipi in tipi non radioattivi. </scherzo >

N2609 è la carta che ha proposto std::decay. Il documento spiega:

poche parole, decay<T>::type è l'identità del tipo di trasformazione tranne se T è un tipo di matrice o un riferimento ad un tipo di funzione. Nei casi il decay<T>::type produce rispettivamente un puntatore o un puntatore a una funzione, .

L'esempio motivante è C++ 03 std::make_pair:

template <class T1, class T2> 
inline pair<T1,T2> make_pair(T1 x, T2 y) 
{ 
    return pair<T1,T2>(x, y); 
} 

che accettano i parametri al valore per rendere letterali stringa funzionano:

std::pair<std::string, int> p = make_pair("foo", 0); 

Se accettato i parametri per riferimento, quindi T1 verrà dedotto come un tipo di matrice e quindi la costruzione di un pair<T1, T2> sarà mal formata.

Ma ovviamente questo porta a notevoli inefficienze. Da qui la necessità di decay, di applicare il set di trasformazioni che si verifica quando si verifica il pass-by-value, consentendo di ottenere l'efficienza di prendere i parametri per riferimento, ma ottenere comunque le trasformazioni di tipo necessarie affinché il codice funzioni con stringhe letterali , tipi di array, tipi di funzione e simili:

template <class T1, class T2> 
inline pair< typename decay<T1>::type, typename decay<T2>::type > 
make_pair(T1&& x, T2&& y) 
{ 
    return pair< typename decay<T1>::type, 
       typename decay<T2>::type >(std::forward<T1>(x), 
              std::forward<T2>(y)); 
} 

Nota: questo non è l'effettiva implementazione C++ 11 make_pair - C++ 11 make_pair scarta anche std::reference_wrapper s.

+0

in qualche modo correlato: decadimento del puntatore di matrici in C. – Alex

+0

"T1 verrà dedotto come un tipo di matrice e quindi la costruzione di una coppia sarà mal formata." qual è il problema qui? – camino

+3

Ho capito, in questo modo otterremo la coppia che può accettare solo stringhe con 4 caratteri – camino

43

Quando si ha a che fare con funzioni di modello che utilizzano parametri di un tipo di modello, spesso si hanno parametri universali. I parametri universali sono quasi sempre riferimenti di un tipo o dell'altro. Sono anche qualificati instabili. Come tale, la maggior parte delle caratteristiche di tipo non funzionano su di loro come ci si aspetterebbe:

template<class T> 
void func(T&& param) { 
    if (std::is_same<T,int>::value) 
     std::cout << "param is an int\n"; 
    else 
     std::cout << "param is not an int\n"; 
} 

int main() { 
    int three = 3; 
    func(three); //prints "param is not an int"!!!! 
} 

http://coliru.stacked-crooked.com/a/24476e60bd906bed

La soluzione è quella di utilizzare std::decay:

template<class T> 
void func(T&& param) { 
    if (std::is_same<typename std::decay<T>::type,int>::value) 
     std::cout << "param is an int\n"; 
    else 
     std::cout << "param is not an int\n"; 
} 

http://coliru.stacked-crooked.com/a/8cbd0119a28a18bd

+12

Non sono contento di questo. 'decay' è molto aggressivo, ad esempio se applicato a un riferimento a array produce un puntatore, tipicamente troppo aggressivo per questo tipo di metaprogrammazione IMHO – dyp

+0

@dyp, cosa c'è di meno "aggressivo" allora? Quali sono le alternative? –

+2

@SergeRogatch Nel caso di "parametri universali"/riferimenti universali/riferimenti di inoltro, vorrei semplicemente 'remove_const_t >', possibilmente racchiuso in una metafunzione personalizzata. – dyp

Problemi correlati