Які причини існування 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.
У роботі з функціями шаблону, які приймають параметри типу шаблону, у вас часто є універсальні параметри. Універсальні параметри майже завжди є посиланнями того чи іншого виду. Вони також кваліфіковані на постійних умовах. Таким чином, більшість типів рис не працює на них так, як ви очікували:
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
дуже агресивний, наприклад, якщо застосувати до посилання масив, він дає вказівник. Зазвичай це занадто агресивно для такого типу метапрограмування IMHO.
remove_const_t< remove_reference_t<T> >
, можливо, загорнутий у власну метафункцію.