Dengan GCC 4.8.0 dirilis, kami memiliki kompiler yang mendukung pengurangan tipe pengembalian otomatis, bagian dari C ++ 14. Dengan -std=c++1y
, saya bisa melakukan ini:
auto foo() { //deduced to be int
return 5;
}
Pertanyaan saya adalah: Kapan saya harus menggunakan fitur ini? Kapan itu perlu dan kapan itu membuat kode lebih bersih?
skenario 1
Skenario pertama yang bisa saya pikirkan adalah kapanpun memungkinkan. Setiap fungsi yang dapat ditulis dengan cara ini seharusnya. Masalah dengan ini adalah bahwa itu mungkin tidak selalu membuat kode lebih mudah dibaca.
Skenario 2
Skenario berikutnya adalah menghindari tipe pengembalian yang lebih kompleks. Sebagai contoh yang sangat ringan:
template<typename T, typename U>
auto add(T t, U u) { //almost deduced as decltype(t + u): decltype(auto) would
return t + u;
}
Saya tidak percaya itu akan benar-benar menjadi masalah, meskipun saya kira memiliki tipe pengembalian secara eksplisit tergantung pada parameter bisa lebih jelas dalam beberapa kasus.
Skenario 3
Selanjutnya, untuk mencegah redundansi:
auto foo() {
std::vector<std::map<std::pair<int, double>, int>> ret;
//fill ret in with stuff
return ret;
}
Dalam C ++ 11, kadang-kadang kita hanya bisa return {5, 6, 7};
menggantikan vektor, tetapi itu tidak selalu berhasil dan kita perlu menentukan tipe di header fungsi dan fungsi tubuh. Ini murni berlebihan, dan pengurangan tipe pengembalian otomatis menyelamatkan kita dari redundansi itu.
Skenario 4
Akhirnya, dapat digunakan sebagai pengganti fungsi yang sangat sederhana:
auto position() {
return pos_;
}
auto area() {
return length_ * width_;
}
Namun, kadang-kadang, kita mungkin melihat fungsinya, ingin tahu tipe yang tepat, dan jika tidak disediakan di sana, kita harus pergi ke titik lain dalam kode, seperti di mana pos_
dinyatakan.
Kesimpulan
Dengan skenario yang ditetapkan, yang mana di antara mereka yang benar-benar terbukti sebagai situasi di mana fitur ini berguna dalam membuat kode lebih bersih? Bagaimana dengan skenario yang saya lupa sebutkan di sini? Peringatan apa yang harus saya ambil sebelum menggunakan fitur ini agar tidak menggigit saya nanti? Apakah ada sesuatu yang baru yang dibawa fitur ini ke meja yang tidak mungkin tanpanya?
Perhatikan bahwa banyak pertanyaan dimaksudkan sebagai bantuan dalam menemukan perspektif untuk menjawab ini.
sumber
->decltype(t+u)
dengan pengurangan otomatis membunuh SFINAE.Jawaban:
C ++ 11 menimbulkan pertanyaan serupa: kapan harus menggunakan pengurangan tipe kembali dalam lambdas, dan kapan harus menggunakan
auto
variabel.Jawaban tradisional untuk pertanyaan dalam C dan C ++ 03 adalah "melintasi batas pernyataan yang kita buat menjadi tipe eksplisit, dalam ekspresi mereka biasanya implisit tetapi kita bisa membuatnya eksplisit dengan gips". C ++ 11 dan C ++ 1y memperkenalkan alat pengurang tipe sehingga Anda dapat mengabaikan tipe di tempat baru.
Maaf, tetapi Anda tidak akan menyelesaikan ini di muka dengan membuat aturan umum. Anda perlu melihat kode tertentu, dan memutuskan sendiri apakah itu membantu keterbacaan untuk menentukan jenis di semua tempat: apakah lebih baik bagi kode Anda untuk mengatakan, "jenis benda ini adalah X", atau lebih baik untuk kode Anda untuk mengatakan, "jenis hal ini tidak relevan untuk memahami bagian kode ini: kompiler perlu tahu dan kami mungkin bisa mengatasinya tetapi kami tidak perlu mengatakannya di sini"?
Karena "keterbacaan" tidak didefinisikan secara objektif [*], dan lebih jauh lagi bervariasi menurut pembaca, Anda memiliki tanggung jawab sebagai penulis / editor sepotong kode yang tidak dapat sepenuhnya dipenuhi oleh panduan gaya. Bahkan sejauh panduan gaya tidak menentukan norma, orang yang berbeda akan lebih suka norma yang berbeda dan cenderung menemukan sesuatu yang tidak dikenal sebagai "kurang dapat dibaca". Jadi keterbacaan aturan gaya yang diusulkan tertentu seringkali hanya dapat dinilai dalam konteks aturan gaya lain yang ada.
Semua skenario Anda (bahkan yang pertama) akan digunakan untuk gaya pengkodean seseorang. Secara pribadi saya menemukan yang kedua menjadi use case yang paling menarik, tetapi meskipun begitu saya mengantisipasi bahwa itu akan tergantung pada alat dokumentasi Anda. Sangat tidak membantu untuk melihat bahwa tipe pengembalian dari templat fungsi adalah
auto
, sedangkan melihatnya didokumentasikan sebagaidecltype(t+u)
membuat antarmuka yang diterbitkan yang dapat (mudah-mudahan) Anda andalkan.[*] Kadang-kadang seseorang mencoba melakukan beberapa pengukuran objektif. Sebagian kecil bahwa siapa pun yang pernah muncul dengan hasil yang signifikan secara statistik dan berlaku umum, mereka sepenuhnya diabaikan oleh programmer yang bekerja, demi naluri penulis tentang apa yang "dapat dibaca".
sumber
1.0 + 27U
kita menegaskan yang terakhir, ketika kita menulis(double)1.0 + (double)27U
kita menegaskan yang pertama. Kesederhanaan fungsi, tingkat duplikasi, menghindaridecltype
semua mungkin berkontribusi untuk itu tetapi tidak ada yang akan menjadi penentu yang andal.auto
secara umum.auto
kata kunci, itu akan menampilkan jenis pengembalian yang sebenarnya.int*
, tetapi yang sebenarnya penting, jika ada, adalah alasannyaint*
karena itulah yangstd::vector<int>::iterator_type
ada pada opsi build Anda saat ini!Secara umum, tipe pengembalian fungsi sangat membantu untuk mendokumentasikan suatu fungsi. Pengguna akan tahu apa yang diharapkan. Namun, ada satu kasus di mana saya pikir bisa lebih baik untuk menjatuhkan tipe pengembalian untuk menghindari redundansi. Berikut ini sebuah contoh:
Contoh ini diambil dari makalah komite resmi N3493 . Tujuan fungsi
apply
adalah untuk meneruskan elemen-elemen daristd::tuple
suatu fungsi dan mengembalikan hasilnya. Ituint_seq
danmake_int_seq
hanya bagian dari implementasi, dan mungkin hanya akan membingungkan setiap pengguna yang mencoba memahami apa fungsinya.Seperti yang Anda lihat, tipe pengembalian tidak lebih dari a
decltype
ekspresi yang dikembalikan. Selain itu,apply_
tidak dimaksudkan untuk dilihat oleh pengguna, saya tidak yakin tentang kegunaan mendokumentasikan tipe kembalinya ketika itu kurang lebih sama dengan yangapply
ada. Saya berpikir bahwa, dalam kasus khusus ini, menjatuhkan tipe pengembalian membuat fungsi lebih mudah dibaca. Perhatikan bahwa tipe pengembalian ini sebenarnya telah dijatuhkan dan diganti olehdecltype(auto)
dalam proposal untuk ditambahkanapply
ke standar, N3915 (juga perhatikan bahwa jawaban awal saya sebelum makalah ini):Namun, sebagian besar waktu, lebih baik menyimpan tipe pengembalian itu. Dalam kasus khusus yang saya jelaskan di atas, tipe pengembalian agak tidak dapat dibaca dan seorang pengguna potensial tidak akan mendapatkan apa pun dari mengetahuinya. Dokumentasi yang baik dengan contoh-contoh akan jauh lebih bermanfaat.
Hal lain yang belum disebutkan: sementara
declype(t+u)
memungkinkan untuk menggunakan ekspresi SFINAE ,decltype(auto)
tidak (meskipun ada proposal untuk mengubah perilaku ini). Ambil contoh afoobar
fungsi yang akan memanggil fungsi anggota tipefoo
jika ada atau panggilbar
fungsi anggota tipe jika ada, dan asumsikan bahwa kelas selalu memiliki ketepatanfoo
ataubar
tetapi tidak keduanya sekaligus:Memanggil
foobar
instanceX
akan menampilkanfoo
saat memanggilfoobar
instanceY
akan menampilkanbar
. Jika Anda menggunakan pengurangan tipe pengembalian otomatis sebagai gantinya (dengan atau tanpadecltype(auto)
), Anda tidak akan mendapatkan ekspresi SFINAE dan memanggilfoobar
instance dari salah satuX
atauY
akan memicu kesalahan waktu kompilasi.sumber
Tidak pernah perlu. Seperti kapan Anda harus- Anda akan mendapatkan banyak jawaban berbeda tentang itu. Saya akan mengatakan tidak sama sekali sampai itu benar-benar bagian yang diterima dari standar dan didukung oleh mayoritas kompiler utama dengan cara yang sama.
Di luar itu, itu akan menjadi argumen agama. Saya pribadi akan mengatakan bahwa tidak pernah memasukkan jenis pengembalian aktual membuat kode lebih jelas, jauh lebih mudah untuk pemeliharaan (saya bisa melihat tanda tangan fungsi dan tahu apa yang dikembalikan vs benar-benar harus membaca kode), dan itu menghilangkan kemungkinan bahwa Anda pikir itu harus mengembalikan satu jenis dan kompiler berpikir yang lain menyebabkan masalah (seperti yang terjadi pada setiap bahasa scripting yang pernah saya gunakan). Saya pikir otomatis adalah kesalahan besar dan itu akan menyebabkan pesanan lebih banyak rasa sakit daripada bantuan. Orang lain akan mengatakan Anda harus menggunakannya setiap saat, karena sesuai dengan filosofi pemrograman mereka. Bagaimanapun, ini jauh dari ruang lingkup untuk situs ini.
sumber
#define
s untuk mengubah C ++ menjadi VB. Fitur umumnya memiliki konsensus yang baik dalam pola pikir bahasa tentang apa yang layak digunakan dan apa yang tidak, sesuai dengan apa yang terbiasa oleh pemrogram bahasa itu. Fitur yang sama dapat hadir dalam berbagai bahasa, tetapi masing-masing memiliki pedoman sendiri tentang penggunaannya.auto
adalah berkah murni. Ini menghilangkan banyak redundansi. Sungguh merepotkan untuk mengulang jenis yang kembali terkadang. Jika Anda ingin mengembalikan lambda, bisa jadi tidak mungkin tanpa menyimpan hasilnya ke dalamstd::function
, yang mungkin menimbulkan beberapa biaya tambahan.auto
sehingga selalu cocok dengan tipe kembali dari fungsi yang diteruskan.auto
untuk trailing tipe kembali.]Itu tidak ada hubungannya dengan kesederhanaan fungsi (sebagai duplikat yang seharusnya dihapus dari pertanyaan ini seharusnya).
Entah jenis pengembalian diperbaiki (tidak digunakan
auto
), atau bergantung secara kompleks pada parameter templat (digunakanauto
dalam kebanyakan kasus, dipasangkan dengandecltype
ketika ada beberapa titik kembali).sumber
Pertimbangkan lingkungan produksi nyata: banyak fungsi dan pengujian unit semua saling bergantung pada jenis pengembalian
foo()
. Sekarang anggaplah bahwa tipe pengembalian perlu diubah untuk alasan apa pun.Jika jenis kembali ada di
auto
mana - mana, dan penelepon kefoo()
dan fungsi terkait menggunakanauto
ketika mendapatkan nilai kembali, perubahan yang perlu dilakukan minimal. Jika tidak, ini bisa berarti jam kerja yang sangat membosankan dan rawan kesalahan.Sebagai contoh dunia nyata, saya diminta untuk mengubah modul dari menggunakan pointer mentah di mana saja menjadi pointer pintar. Memperbaiki unit test lebih menyakitkan daripada kode sebenarnya.
Meskipun ada cara lain yang bisa ditangani, penggunaan
auto
tipe pengembalian sepertinya cocok.sumber
decltype(foo())
?typedef
deklarasi s dan alias (mis.using
Deklarasi tipe) lebih baik dalam kasus ini, khususnya ketika mereka di-scoped kelas.Saya ingin memberikan contoh di mana tipe pengembalian otomatis sempurna:
Bayangkan Anda ingin membuat alias pendek untuk panggilan fungsi yang panjang berikutnya. Dengan otomatis Anda tidak perlu mengurus jenis pengembalian asli (mungkin akan berubah di masa mendatang) dan pengguna dapat mengklik fungsi asli untuk mendapatkan jenis pengembalian nyata:
PS: Tergantung pada pertanyaan ini .
sumber
Untuk skenario 3 saya akan mengubah jenis tanda tangan fungsi kembali dengan variabel lokal untuk dikembalikan sekitar. Itu akan membuatnya lebih jelas untuk pemrogram klien dan kembali fungsi. Seperti ini:
Skenario 3 Untuk mencegah redundansi:
Ya, itu tidak memiliki kata kunci otomatis tetapi kepala sekolah adalah sama untuk mencegah redundansi dan memberikan programmer yang tidak memiliki akses ke sumber waktu yang lebih mudah.
sumber
vector<map<pair<int,double>,int>
dan kemudian menggunakannya.