Apa itu std :: decay dan kapan harus digunakan?

185

Apa alasan keberadaannya std::decay? Dalam situasi apa std::decayberguna?

Eric Javier Hernandez Saura
sumber
3
Ini digunakan dalam pustaka standar misalnya ketika meneruskan argumen ke utas. Nilai tersebut perlu disimpan , berdasarkan nilai, sehingga Anda tidak dapat menyimpan mis. Array. Sebagai gantinya, sebuah pointer disimpan dan seterusnya. Ini juga merupakan metafungsi yang meniru penyesuaian tipe parameter fungsi.
dyp
3
decay_t<decltype(...)>adalah kombinasi yang bagus, untuk melihat apa yang autoakan disimpulkan.
Marc Glisse
58
Variabel radioaktif? :)
saiarcot895
7
std :: decay () dapat melakukan tiga hal. 1 Ia mampu mengonversi array T ke T *; 2. Dapat menghapus kualifikasi dan referensi cv; 3. Mengkonversi fungsi T ke T *. misalnya pembusukan (void (char)) -> void (*) (char). Sepertinya tidak ada yang menyebutkan penggunaan ketiga dalam jawaban.
r0ng
1
Syukurlah kita belum punya quark di c ++
Wormer

Jawaban:

192

<canda> Ini jelas digunakan untuk meluruhkan std::atomictipe radioaktif menjadi non-radioaktif. </joke>

N2609 adalah makalah yang diusulkan std::decay. Makalah ini menjelaskan:

Sederhananya, decay<T>::typeadalah tipe-transformasi kecuali jika T adalah tipe array atau referensi ke tipe fungsi. Dalam kasus tersebut decay<T>::type, masing-masing menghasilkan penunjuk atau penunjuk ke fungsi.

Contoh yang memotivasi adalah 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); 
}

yang menerima parameternya dengan nilai untuk membuat string literal berfungsi:

std::pair<std::string, int> p = make_pair("foo", 0);

Jika ia menerima parameternya dengan referensi, maka T1akan disimpulkan sebagai tipe array, dan kemudian membangun pair<T1, T2>akan menjadi salah bentuk.

Tapi jelas ini menyebabkan inefisiensi yang signifikan. Oleh karena itu perlunya decay, untuk menerapkan serangkaian transformasi yang terjadi ketika nilai demi nilai terjadi, memungkinkan Anda untuk mendapatkan efisiensi dengan mengambil parameter dengan referensi, tetapi masih mendapatkan jenis transformasi yang diperlukan untuk kode Anda untuk bekerja dengan string literal, tipe array, tipe fungsi dan sejenisnya:

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

Catatan: ini bukan make_pairimplementasi C ++ 11 yang sebenarnya - C ++ 11 make_pairjuga membuka std::reference_wrappers.

TC
sumber
"T1 akan dideduksi sebagai tipe array, dan kemudian membangun pasangan <T1, T2> akan terbentuk buruk." Apa masalah yang terjadi di sini?
camino
6
Saya mengerti, dengan cara ini kita akan mendapatkan pasangan <char [4], int> yang hanya dapat menerima string dengan 4 karakter
camino
@camino Saya tidak mengerti, apakah Anda mengatakan bahwa tanpa std :: pembusukan bagian pertama dari pasangan akan menempati 4 byte untuk empat karakter bukan satu pointer ke char? Apakah itu yang dilakukan oleh std :: forward? Menghentikannya dari pembusukan dari array ke pointer?
Zebrafish
3
@ Zebrafish Ini adalah peluruhan array. Sebagai contoh: template <typename T> void f (T &); f ("abc"); T adalah char (&) [4], tetapi templat <typename T> void f (T); f ("abc"); T adalah char *; Anda juga dapat menemukan penjelasan di sini: stackoverflow.com/questions/7797839/…
camino
69

Ketika berhadapan dengan fungsi templat yang mengambil parameter dari tipe templat, Anda sering memiliki parameter universal. Parameter universal hampir selalu merupakan referensi dari satu jenis atau lainnya. Mereka juga memenuhi syarat const-volatile. Dengan demikian, sebagian besar sifat tipe tidak berfungsi seperti yang Anda harapkan:

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

Solusinya di sini adalah menggunakan 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

Mooing Duck
sumber
14
Saya tidak senang dengan ini. decaysangat agresif, misalnya jika diterapkan pada referensi ke array itu menghasilkan pointer. Biasanya terlalu agresif untuk metaprogramming IMHO semacam ini.
dyp
@yp, lalu apa yang kurang "agresif"? Apa alternatifnya?
Serge Rogatch
5
@SergeRogatch Dalam kasus "parameter universal" / referensi universal / referensi penerusan, saya baru saja remove_const_t< remove_reference_t<T> >, mungkin dibungkus dengan metafungsi khusus.
dyp
1
di mana param bahkan digunakan? Ini adalah argumen dari func tapi saya tidak melihatnya digunakan di mana saja
savram
2
@savram: Pada bagian kode ini: tidak. Kami hanya memeriksa jenisnya, bukan nilainya. Semuanya akan berfungsi dengan baik jika tidak lebih baik jika kita menghapus nama parameter.
Mooing Duck