В чем причины существования std::decay
?
В каких ситуациях полезно std::decay
?
Что такое std:: decay и когда он должен использоваться?
Ответ 1
<joke> Он, очевидно, используется для разложения радиоактивных типов std::atomic
на нерадиоактивные. </joke>
N2609 - это документ, который предложил std::decay
. В документе объясняется:
Проще говоря,
decay<T>::type
- это преобразование типа идентичности, кроме если T - тип массива или ссылка на тип функции. В тех случаиdecay<T>::type
дают указатель или указатель на функцию, соответственно.
Мотивирующим примером является С++ 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);
}
который принял свои параметры по значению для создания строковых литералов:
std::pair<std::string, int> p = make_pair("foo", 0);
Если он принимает свои параметры по ссылке, то T1
будет выводиться как тип массива, а затем создание pair<T1, T2>
будет плохо сформировано.
Но, очевидно, это приводит к значительной неэффективности. Следовательно, необходимо использовать decay
, чтобы применить набор преобразований, возникающих при переходе по значению, что позволяет вам эффективно использовать параметры по ссылке, но при этом получать преобразования типов, необходимые для работы вашего кода с строковые литералы, типы массивов, типы функций и т.п.:
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));
}
Примечание: это не реальная реализация С++ 11 make_pair
- С++ 11 make_pair
также распаковывает std::reference_wrapper
s.
Ответ 2
При работе с функциями шаблона, которые принимают параметры типа шаблона, вы часто имеете универсальные параметры. Универсальные параметры почти всегда являются ссылками того или иного типа. Они также обладают непреодолимой силой. Таким образом, большинство черт типа не работают на них, как вы ожидали:
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
Решением здесь является использование 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";
}