Каковы причины существования std::decay
? В каких ситуациях std::decay
пригодится?
decay_t<decltype(...)>
хорошая комбинация, чтобы увидеть, что auto
можно сделать.
Каковы причины существования std::decay
? В каких ситуациях std::decay
пригодится?
decay_t<decltype(...)>
хорошая комбинация, чтобы увидеть, что auto
можно сделать.
Ответы:
<joke> Очевидно, он используется для распада радиоактивных std::atomic
типов на нерадиоактивные. </joke>
N2609 - это предложенная бумага std::decay
. В документе объясняется:
Проще говоря,
decay<T>::type
это преобразование типа идентичности, кроме случаев, когда T является типом массива или ссылкой на тип функции. В этих случаяхdecay<T>::type
возвращает указатель или указатель на функцию соответственно.
В качестве мотивирующего примера можно привести 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);
}
который принимает параметры по значению, чтобы строковые литералы работали:
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));
}
Примечание: это не реальная реализация C ++ 11 make_pair
- C ++ 11 make_pair
также разворачивает std::reference_wrapper
s.
Имея дело с шаблонными функциями, которые принимают параметры типа шаблона, у вас часто есть универсальные параметры. Универсальные параметры почти всегда являются ссылками того или иного рода. Они также квалифицированы как const-volatile. Таким образом, большинство свойств типов работают с ними не так, как вы ожидали:
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";
}
decay
очень агрессивен, например, если применяется к ссылке на массив, он дает указатель. ИМХО, как правило, это слишком агрессивно для такого рода метапрограммирования.
remove_const_t< remove_reference_t<T> >
, возможно, обернул пользовательскую метафункцию.
int
, const int
, int&
, const int&
, int&&
, const int&&
, volatile int
, volatile int&
, volatile int&&
, и т.д., все разные.