Quelles sont les raisons de l'existence de std::decay
? Dans quelles situations est-il std::decay
utile?
decay_t<decltype(...)>
est une belle combinaison, pour voir ce que l' auto
on en déduit.
Quelles sont les raisons de l'existence de std::decay
? Dans quelles situations est-il std::decay
utile?
decay_t<decltype(...)>
est une belle combinaison, pour voir ce que l' auto
on en déduit.
Réponses:
<joke> Il est évidemment utilisé pour décomposer les std::atomic
types radioactifs en types non radioactifs. </joke>
N2609 est le papier qui a proposé std::decay
. Le papier explique:
En termes simples,
decay<T>::type
est la transformation du type d'identité sauf si T est un type de tableau ou une référence à un type de fonction. Dans ces cas, ledecay<T>::type
produit un pointeur ou un pointeur vers une fonction, respectivement.
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 fonctionner les littéraux de chaîne:
std::pair<std::string, int> p = make_pair("foo", 0);
S'il a accepté ses paramètres par référence, alors T1
sera déduit comme un type de tableau, puis la construction de a pair<T1, T2>
sera mal formée.
Mais cela conduit évidemment à des inefficacités significatives. D'où la nécessité decay
, d'appliquer l'ensemble des transformations qui se produisent lorsque le passage par valeur se produit, vous permettant d'obtenir l'efficacité de prendre les paramètres par référence, tout en obtenant les transformations de type nécessaires pour que votre code fonctionne avec des littéraux de chaîne, types de tableaux, types de fonctions et autres:
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));
}
Remarque: ce n'est pas l' make_pair
implémentation réelle de C ++ 11 - le C ++ 11 make_pair
déballe également std::reference_wrapper
s.
Lorsque vous traitez avec 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 pour la volatilité constante. En tant que tel, la plupart des traits de type ne fonctionnent pas sur eux comme vous vous en doutez:
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 ici est d'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";
}
decay
est très agressif, par exemple s'il est appliqué à une référence à un tableau, il produit un pointeur. Il est généralement trop agressif pour ce type de métaprogrammation à mon humble avis.
remove_const_t< remove_reference_t<T> >
, éventuellement enveloppé dans une métafonction personnalisée.