Каковы причины существования 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_wrappers.
Имея дело с шаблонными функциями, которые принимают параметры типа шаблона, у вас часто есть универсальные параметры. Универсальные параметры почти всегда являются ссылками того или иного рода. Они также квалифицированы как 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&&, и т.д., все разные.