2014-09-08 2 views
120

Quelles sont les raisons de l'existence de std::decay? Dans quelles situations std::decay est-il utile?Qu'est-ce que std :: decay et quand faut-il l'utiliser?

+3

Il est utilisé dans la bibliothèque standard, par ex. lors de la transmission d'arguments à un thread. Ceux-ci doivent être * stockés *, en valeur, de sorte que vous ne puissiez pas les stocker, par ex. tableaux. Au lieu de cela, un pointeur est stocké et ainsi de suite. C'est aussi une métafonction qui imite les ajustements de type de paramètre de fonction. – dyp

+3

'decay_t ' est une bonne combinaison, pour voir ce que 'auto' pourrait en déduire. –

+0

[meta.trans.other] états: Ce comportement est similaire à [...= Plusieurs] conversions appliquées lorsqu'une expression lvalue est utilisé comme rvalue, mais aussi des bandes cv-qualifiés de types de classe ** pour de modèle plus près par valeur ** passage d 'arguments. » – dyp

Répondre

116

<blague> Il est évidemment utilisé pour se désintégrer radioactif std::atomic types dans les non-radioactifs. </blague >

N2609 est le papier qui a proposé std::decay. Le document explique:

Autrement dit, decay<T>::type est du type à transformation ayant une identité sauf si T est un type tableau ou une référence à un type de fonction. Dans les cas , le decay<T>::type donne respectivement un pointeur ou un pointeur sur une fonction, .

L'exemple motivant est 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); 
} 

qui a accepté ses paramètres par valeur pour faire des chaînes littérales travail:

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

Si elle a accepté ses paramètres par référence, alors T1 sera déduit comme un type de tableau, puis la construction d'un pair<T1, T2> sera mal formé. Mais, évidemment, cela conduit à des inefficacités significatives. D'où la nécessité de decay, d'appliquer l'ensemble des transformations qui se produisent lorsque la valeur de passe se produit, vous permettant d'obtenir l'efficacité de prendre les paramètres par référence, mais obtenez toujours les transformations de type nécessaires pour que votre code fonctionne avec des littéraux de chaîne , les types de tableaux, les types de fonctions et similaires:

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)); 
} 

Note: ce n'est pas réelle C++ 11 make_pair mise en œuvre - 11 C++ make_pair aussi unwraps std::reference_wrapper s.

+0

quelque peu liée: déclinaison en pointeur de tableaux dans C. – Alex

+0

"T1 sera déduit comme un type de tableau, puis la construction d'une paire sera mal formée." Quel est le problème ici? – camino

+3

Je comprends, de cette façon, nous obtiendrons la paire qui ne peut accepter que des chaînes avec 4 caractères – camino

43

Lorsque vous manipulez des fonctions de modèle qui prennent des paramètres d'un type de modèle, vous avez souvent des paramètres universels. Les paramètres universels sont presque toujours des références d'une sorte ou d'une autre. Ils sont également qualifiés de const-volatile. En tant que tel, la plupart des caractères de type ne fonctionnent pas sur eux comme on pouvait s'y attendre:

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 solution est ici pour utiliser 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

Je ne suis pas content de ça. 'decay' est très agressif, par exemple s'il est appliqué à une référence à tableau il donne un pointeur.Il est généralement trop agressif pour ce genre de métaprogrammation IMHO – dyp

+0

@dyp, quoi de moins "agressif" alors? Quelles sont les alternatives? –

+2

@SergeRogatch Dans le cas de "paramètres universels"/références universelles/renvois, je supprimerais simplement 'remove_const_t >', éventuellement enveloppé dans une métafonction personnalisée. – dyp

Questions connexes