Що таке std :: розпад і коли його слід використовувати?


185

Які причини існування std::decay? У яких ситуаціях std::decayкорисно?


3
Він використовується в стандартній бібліотеці, наприклад, при передачі аргументів до потоку. Їх потрібно зберігати за значенням, тому ви не можете зберігати, наприклад, масиви. Натомість вказівник зберігається тощо. Це також метафункція, яка імітує коригування типу параметрів функції.
dyp

3
decay_t<decltype(...)>це приємне поєднання, щоб побачити, що autoможе вивести.
Марк Глісс

58
Радіоактивні змінні? :)
saiarcot895

7
std :: decay () може робити три речі. 1 Він здатний перетворити масив T в T *; 2. Він може видалити рейтинг cv і посилання; 3. Він перетворює функцію T в T *. наприклад розпад (void (char)) -> void (*) (char). Здається, ніхто не згадав про третє використання у відповідях.
r0ng

1
Слава богу, у нас ще немає кварків в c ++
гірше

Відповіді:


192

<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.


"T1 буде виведено як тип масиву, і тоді побудова пари <T1, T2> буде неправильно сформована." в чому тут проблема?
camino

6
Я розумію, таким чином ми отримаємо пару <char [4], int> яка може приймати лише рядки з 4 символами
camino

@camino Я не розумію, ти кажеш, що без std :: розпад перша частина пари займе 4 байти на чотири символи замість одного вказівника на char? Це те, що робить std :: форвард? Зупиняє його від розпаду масиву до вказівника?
Зебрафіш

3
@Zebrafish Це розпад масиву. Наприклад: шаблон <typename T> void f (T &); f ("abc"); T - char (&) [4], але шаблон <typename T> void f (T); f ("abc"); T - char *; Ви також можете знайти пояснення тут: stackoverflow.com/questions/7797839/…
camino

69

У роботі з функціями шаблону, які приймають параметри типу шаблону, у вас часто є універсальні параметри. Універсальні параметри майже завжди є посиланнями того чи іншого виду. Вони також кваліфіковані на постійних умовах. Таким чином, більшість типів рис не працює на них так, як ви очікували:

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";
}

http://coliru.stacked-crooked.com/a/8cbd0119a28a18bd


14
Я не задоволений цим. decayдуже агресивний, наприклад, якщо застосувати до посилання масив, він дає вказівник. Зазвичай це занадто агресивно для такого типу метапрограмування IMHO.
dyp

@dyp, що тоді менш "агресивне"? Що таке альтернативи?
Серж Рогач

5
@SergeRogatch У випадку "універсальних параметрів" / універсальних посилань / посилань для переадресації я просто remove_const_t< remove_reference_t<T> >, можливо, загорнутий у власну метафункцію.
dyp

1
де парам навіть використовується? Це аргумент func, але я не бачу, що він використовується ніде
savram

2
@savram: У цих фрагментах коду: це не так. Ми перевіряємо лише тип, а не значення. Все повинно працювати добре, якщо не краще, якщо ми видалили ім’я параметра.
Mooing Duck
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.