Tampaknya itu auto
adalah fitur yang cukup signifikan untuk ditambahkan dalam C ++ 11 yang tampaknya mengikuti banyak bahasa yang lebih baru. Seperti bahasa seperti Python, saya belum melihat adanya deklarasi variabel eksplisit (saya tidak yakin apakah mungkin menggunakan standar Python).
Apakah ada kelemahan menggunakan auto
untuk mendeklarasikan variabel daripada secara eksplisit mendeklarasikannya?
c++
c++11
type-inference
auto
DxAlpha
sumber
sumber
Jawaban:
Anda hanya bertanya tentang kekurangannya, jadi saya menyoroti beberapa di antaranya. Bila digunakan dengan baik,
auto
memiliki beberapa keunggulan juga. Kekurangannya adalah karena mudahnya penyalahgunaan, dan dari meningkatnya potensi kode untuk berperilaku dengan cara yang tidak disengaja.Kelemahan utama adalah bahwa, dengan menggunakan
auto
, Anda tidak perlu tahu jenis objek yang dibuat. Ada juga kesempatan di mana programmer mungkin mengharapkan kompiler untuk menyimpulkan satu jenis, tetapi kompiler dengan tegas menyimpulkan yang lain.Diberikan deklarasi seperti
Anda tidak perlu memiliki pengetahuan tentang jenis apa
result
itu. Itu mungkin sebuahint
. Mungkin sebuah pointer. Mungkin sesuatu yang lain. Semua itu mendukung operasi yang berbeda. Anda juga dapat secara dramatis mengubah kode dengan perubahan kecil sepertikarena, tergantung pada kelebihan apa yang ada untuk
CallSomeFunction()
jenis hasil mungkin sama sekali berbeda - dan karena itu kode selanjutnya mungkin berperilaku sangat berbeda dari yang dimaksudkan. Anda mungkin tiba-tiba memicu pesan kesalahan dalam kode selanjutnya (mis. Kemudianint
mencoba untuk mengubah referensi , mencoba mengubah sesuatu yang sekarangconst
). Perubahan yang lebih menyeramkan adalah saat perubahan Anda melewati kompiler, tetapi kode selanjutnya berperilaku dengan cara yang berbeda dan tidak diketahui - mungkin buggy.Karena itu, tidak memiliki pengetahuan eksplisit tentang jenis beberapa variabel membuatnya lebih sulit untuk membenarkan klaim bahwa kode berfungsi sebagaimana dimaksud. Ini berarti lebih banyak upaya untuk membenarkan klaim "cocok untuk tujuan" dalam domain kritis tinggi (mis. Keselamatan kritis atau misi kritis).
Kelemahan lainnya, yang lebih umum, adalah godaan bagi seorang programmer untuk digunakan
auto
sebagai instrumen tumpul untuk memaksa kode dikompilasi, daripada memikirkan apa yang dilakukan kode, dan bekerja untuk memperbaikinya.sumber
auto
, maka sebagian besar bahasa yang diketik bebek mengalami kekurangan seperti itu oleh desain!CallSomeFunction()
mengembalikan jenis yang berbeda tergantung pada urutan argumennya, itu adalah cacat desainCallSomeFunction()
, bukan masalahauto
. Jika Anda tidak membaca dokumentasi fungsi yang Anda gunakan sebelum menggunakannya, itu adalah kesalahan programmer, bukan masalahauto
. - Tapi saya mengerti bahwa Anda berperan sebagai penasihat setan di sini, hanya saja Nir Friedman memiliki kasus yang jauh lebih baik.T CallSomeFunction(T, int, int)
cacat desain? Jelas itu "mengembalikan jenis yang berbeda tergantung pada urutan argumennya."auto
, Anda tidak perlu tahu jenis objek yang sedang dibuat." Bisakah Anda menguraikan mengapa ini merupakan masalahauto
, dan bukan masalah dengan temporer subekspresi? Mengapa ituauto result = foo();
buruk, tetapifoo().bar()
tidak?Ini bukan kelemahan
auto
dengan cara berprinsip persis, tetapi dalam hal praktis tampaknya menjadi masalah bagi sebagian orang. Pada dasarnya, beberapa orang entah: a) memperlakukanauto
sebagai penyelamat untuk jenis dan mematikan otak mereka saat menggunakannya, atau b) lupa bahwaauto
selalu menyimpulkan jenis nilai. Ini menyebabkan orang melakukan hal-hal seperti ini:Ups, kami baru saja menyalin beberapa objek. Seringkali bug atau kinerja gagal. Kemudian, Anda bisa mengayunkan ke arah lain juga:
Sekarang Anda mendapatkan referensi yang menggantung. Masalah-masalah ini tidak disebabkan oleh
auto
sama sekali, jadi saya tidak menganggapnya sebagai argumen yang sah untuk menentangnya. Tapi sepertinyaauto
membuat masalah ini lebih umum (dari pengalaman pribadi saya), untuk alasan yang saya sebutkan di awal.Saya pikir ketika orang-orang akan menyesuaikan, dan memahami pembagian kerja:
auto
menyimpulkan jenis yang mendasarinya, tetapi Anda masih ingin berpikir tentang referensi-ke-kekakuan dan kekekalan. Tapi itu butuh sedikit waktu.sumber
std::vector
). Menjadi mahal untuk menyalin bukan milik kelas, tetapi benda individu. Jadimethod_that_returns_reference
mungkin merujuk ke objek kelas yang memiliki copy constructor, tetapi objek mana yang cukup mahal untuk menyalin (dan tidak dapat dipindahkan dari).std::vector
? (Karena itu mungkin, ya, atau karena Anda tidak mengontrol kelas, tapi bukan itu intinya) Jika mahal untuk menyalin, (dan tidak memiliki sumber daya, karena dapat disalin), mengapa tidak menggunakan SAP pada objek? Lokalitas data sudah terbunuh oleh ukuran objek.= delete
kelebihan itu. Padahal yang lebih umum apa yang Anda katakan adalah solusi. Ini adalah topik yang telah saya jelajahi, jika Anda tertarik: nirfriedman.com/2016/01/18/… .Jawaban lain menyebutkan kelemahan seperti "Anda tidak benar-benar tahu apa jenis variabelnya." Saya akan mengatakan bahwa ini sebagian besar terkait dengan konvensi penamaan ceroboh dalam kode. Jika antarmuka Anda diberi nama yang jelas, Anda tidak perlu peduli dengan tipe yang tepat. Tentu,
auto result = callSomeFunction(a, b);
tidak banyak bercerita. Tetapiauto valid = isValid(xmlFile, schema);
memberitahu Anda cukup untuk digunakanvalid
tanpa harus peduli apa jenis persisnya. Lagi pula, hanya denganif (callSomeFunction(a, b))
, Anda tidak akan tahu jenisnya juga. Sama dengan objek sementara subekspresi lainnya. Jadi saya tidak menganggap ini sebagai kelemahan nyataauto
.Saya akan mengatakan kelemahan utamanya adalah bahwa kadang-kadang, tipe pengembalian yang tepat bukanlah yang ingin Anda kerjakan. Akibatnya, kadang-kadang jenis pengembalian aktual berbeda dari jenis pengembalian "logis" sebagai detail implementasi / optimisasi. Template ekspresi adalah contoh utama. Katakanlah kita punya ini:
Secara logis, kita berharap
SomeType
demikianVector
, dan kita pasti ingin memperlakukannya seperti itu dalam kode kita. Namun, ada kemungkinan bahwa untuk tujuan optimasi, perpustakaan aljabar yang kami gunakan mengimplementasikan templat ekspresi, dan tipe pengembalian sebenarnya adalah ini:Sekarang, masalahnya adalah bahwa
MultExpression<Matrix, Vector>
kemungkinan besar akan menyimpanconst Matrix&
dan secaraconst Vector&
internal; ia mengharapkan bahwa itu akan dikonversi keVector
sebelum akhir dari ekspresi penuhnya. Jika kita memiliki kode ini, semuanya baik-baik saja:Namun, jika kami digunakan di
auto
sini, kami bisa mendapat masalah:sumber
auto
memiliki sedikit kekurangan, jadi saya berdiri dengan kekuatan itu. Dan contoh lain dari proxy dll. Termasuk berbagai "pembangun string" dan objek serupa yang ditemukan di DSL.auto
sebelumnya, khususnya dengan perpustakaan Eigen. Ini terutama rumit karena masalah sering tidak muncul dalam debug build.auto
can juga menggigit saat menggunakan perpustakaan matriks Armadillo , yang banyak menggunakan meta-pemrograman templat untuk keperluan optimasi. Untungnya para pengembang telah menambahkan fungsi .eval () yang dapat digunakan untuk menghindari masalah denganauto
auto
biasanya melibatkan semacam jenis pemeriksaan (dan memercikauto
di mana-mana menghilangkan jenis keamanan seperti halnya di tempat lain). Itu bukan perbandingan yang bagus.Salah satu kelemahan adalah bahwa kadang-kadang Anda tidak dapat mendeklarasikan
const_iterator
denganauto
. Anda akan mendapatkan iterator biasa (bukan const) dalam contoh kode yang diambil dari pertanyaan ini :sumber
iterator
dalam hal apapun karena peta Anda bukanconst
. jika Anda ingin mengubahnya menjadiconst_iterator
, tentukan jenis variabel secara eksplisit seperti biasa, atau ekstrak metode sehingga peta Anda adalah konst dalam konteks Andafind
. (Saya lebih suka yang terakhir. SRP.)auto city_it = static_cast<const auto&>(map).find("New York")
? atau, dengan C ++ 17auto city_if = std::as_const(map).find("New York")
,.Itu membuat kode Anda sedikit lebih sulit, atau membosankan, untuk dibaca. Bayangkan sesuatu seperti itu:
Sekarang, untuk mengetahui tipe output, Anda harus melacak tanda tangan
doSomethingWithData
fungsi.sumber
auto it = vec.begin();
jauh lebih mudah dibaca daripadastd::vector<std::wstring>::iterator it = vec.begin();
misalnya.Seperti pengembang ini , saya benci
auto
. Atau lebih tepatnya, aku benci bagaimana orang menyalahgunakanauto
.Saya berpendapat (kuat)
auto
untuk membantu Anda menulis kode generik, bukan untuk mengurangi mengetik .C ++ adalah bahasa yang tujuannya membiarkan Anda menulis kode yang kuat, bukan untuk meminimalkan waktu pengembangan.
Ini cukup jelas dari banyak fitur C ++, tapi sayangnya beberapa yang lebih baru seperti
auto
itu mengurangi mengetik orang yang menyesatkan sehingga berpikir mereka harus mulai malas mengetik.Pada
auto
masa pra- hari, orang menggunakantypedef
s, yang sangat bagus karenatypedef
memungkinkan perancang perpustakaan untuk membantu Anda mengetahui apa yang seharusnya menjadi tipe pengembalian, sehingga perpustakaan mereka berfungsi seperti yang diharapkan. Saat Anda menggunakanauto
, Anda mengambil kontrol itu dari desainer kelas dan alih-alih meminta kompiler untuk mencari tahu apa yang harus menjadi tipe, yang menghapus salah satu alat C ++ paling kuat dari kotak alat dan berisiko melanggar kode mereka.Secara umum, jika Anda menggunakan
auto
, itu harus karena kode Anda berfungsi untuk semua jenis yang masuk akal , bukan karena Anda terlalu malas untuk menuliskan jenis yang harusnya bekerja. Jika Anda menggunakanauto
sebagai alat untuk membantu kemalasan, maka yang terjadi adalah Anda akhirnya mulai memperkenalkan bug halus di program Anda, biasanya disebabkan oleh konversi tersirat yang tidak terjadi karena Anda menggunakannyaauto
.Sayangnya, bug ini sulit untuk diilustrasikan dalam contoh singkat di sini karena singkatnya mereka membuat mereka kurang meyakinkan daripada contoh aktual yang muncul dalam proyek pengguna - namun, mereka muncul dengan mudah dalam kode template-berat yang mengharapkan konversi implisit tertentu untuk mengambil tempat.
Jika Anda ingin contoh, ada satu di sini . Namun, sedikit catatan: sebelum tergoda untuk melompat dan mengkritik kode: perlu diingat bahwa banyak perpustakaan terkenal dan dewasa telah dikembangkan di sekitar konversi implisit, dan mereka ada di sana karena mereka memecahkan masalah yang bisa sulit jika bukan tidak mungkin untuk memecahkan sebaliknya. Cobalah mencari solusi yang lebih baik sebelum mengkritik mereka.
sumber
which was great because typedef allowed the designer of the library to help you figure out what the return type should be, so that their library works as expected. When you use auto, you take away that control from the class's designer and instead ask the compiler to figure out what the type should be
IMO bukan alasan yang bagus. IDE terkini, Visual Studio 2015 misalnya, memungkinkan Anda untuk memeriksa jenis variabel dengan mengarahkan kursorauto
. Ini * persis * sama dengan yangtypedef
ada.typename std::iterator_traits<It>::value_type
. (2) Intinya adalah bahwa jenis yang disimpulkan tidak harus "persis sama" dengan jenis yang benar yang dimaksudkan oleh perancang kode sebelumnya; dengan menggunakanauto
, Anda menghilangkan kemampuan desainer untuk menentukan jenis yang benar.vector<bool>
omong kosong" ... maaf? Bagaimana menurut Andabitset
diimplementasikan? Atau apakah Anda menganggap wadah bit sebagai omong kosong sama sekali ?!auto
tidak memiliki kelemahan per se , dan saya menganjurkan untuk (goyah) menggunakannya di mana-mana dalam kode baru. Ini memungkinkan kode Anda untuk secara konsisten mengetik-cek, dan secara konsisten menghindari pemotongan diam. (JikaB
berasal dariA
dan fungsi yang kembaliA
tiba-tiba kembaliB
, makaauto
berperilaku seperti yang diharapkan untuk menyimpan nilai pengembaliannya)Meskipun, kode lama pre-C ++ 11 dapat mengandalkan konversi implisit yang disebabkan oleh penggunaan variabel yang diketik secara eksplisit. Mengubah variabel yang diketik secara eksplisit untuk
auto
dapat mengubah perilaku kode , jadi sebaiknya Anda berhati-hati.sumber
auto
memiliki kekurangan per se (atau setidaknya - banyak yang berpikir demikian). Pertimbangkan contoh yang diberikan dalam pertanyaan kedua dalam diskusi panel ini dengan Sutter, Alexandrescu dan Meyers: Jika Anda memilikiauto x = foo(); if (x) { bar(); } else { baz(); }
danfoo()
kembalibool
- apa yang terjadi jikafoo()
perubahan untuk mengembalikan enum (tiga opsi, bukan dua)? Theauto
kode akan terus bekerja, tapi menghasilkan hasil yang tidak diharapkan.bool
alih-alihauto
mengubah apa pun jika ada enum yang tidak dicopot? Saya mungkin salah (tidak dapat memeriksa di sini), tetapi saya pikir satu-satunya perbedaan adalah bahwa konversibool
terjadi pada deklarasi variabel dan bukan pada evaluasi kondisi dalamif
. Jikaenum
scoped, maka konversi kebool
tidak akan terjadi tanpa pemberitahuan eksplisit.Kata kunci
auto
cukup menyimpulkan jenis dari nilai pengembalian. Oleh karena itu, ini tidak setara dengan objek Python, misKarena
auto
dideduksi selama kompilasi, itu tidak akan memiliki kekurangan saat runtime sama sekali.sumber
type()
dengan python. Itu menyimpulkan jenis, itu tidak membuat variabel baru dari jenis itu.decltype
.auto
khusus untuk penugasan variabel.Apa yang tidak disebutkan di sini sejauh ini, tetapi untuk dirinya sendiri bernilai jawaban jika Anda bertanya kepada saya.
Karena (walaupun semua orang harus sadar bahwa
C != C++
) kode yang ditulis dalam C dapat dengan mudah dirancang untuk menyediakan basis untuk kode C ++ dan karenanya dirancang tanpa terlalu banyak upaya untuk menjadi kompatibel dengan C ++, ini bisa menjadi persyaratan untuk desain.Saya tahu tentang beberapa aturan di mana beberapa konstruksi yang didefinisikan dengan baik
C
tidak valid untukC++
dan sebaliknya. Tapi ini hanya akan menghasilkan executable yang rusak dan klausa UB yang berlaku berlaku yang paling sering diperhatikan oleh perulangan aneh yang mengakibatkan crash atau apa pun (atau bahkan mungkin tetap tidak terdeteksi, tetapi itu tidak masalah di sini).Tapi
auto
apakah pertama kali 1 ini berubah!Bayangkan Anda digunakan
auto
sebagai specifier kelas penyimpanan sebelum dan mentransfer kode. Bahkan tidak perlu (tergantung pada cara itu digunakan) "istirahat"; itu sebenarnya bisa mengubah perilaku program secara diam-diam.Itu sesuatu yang harus diingat.
1 Setidaknya pertama kali aku sadar.
sumber
int
" di C, Anda layak mendapatkan semua hal buruk yang akan Anda dapatkan dari ini. Dan jika Anda tidak mengandalkan itu, menggunakanauto
sebagai specifier kelas penyimpanan bersama tipe akan memberi Anda kesalahan kompilasi yang bagus di C ++ (yang merupakan Good Thing dalam kasus ini).Salah satu alasan yang dapat saya pikirkan adalah bahwa Anda kehilangan kesempatan untuk memaksa kelas yang dikembalikan. Jika fungsi atau metode Anda mengembalikan panjang 64 bit, dan Anda hanya menginginkan 32 unsigned int, maka Anda kehilangan kesempatan untuk mengontrolnya.
sumber
Seperti yang saya jelaskan dalam jawaban
auto
ini kadang-kadang dapat menghasilkan situasi funky yang tidak Anda inginkan. Anda harus secara eksplisit mengatakanauto&
memiliki tipe referensi saat melakukan sajaauto
dapat membuat tipe pointer. Ini dapat menyebabkan kebingungan dengan menghilangkan specifier secara bersamaan, menghasilkan salinan referensi alih-alih referensi yang sebenarnya.sumber
auto
terjadi, jangan pernah menyimpulkan referensi atauconst
tipe. Untukauto
referensi, sebaiknya Anda gunakanauto&&
. (referensi universal) Jika jenisnya tidak murah untuk disalin atau memiliki sumber daya, maka jenis tersebut seharusnya tidak dapat disalin untuk memulai.Contoh menjengkelkan lainnya:
menghasilkan peringatan (
comparison between signed and unsigned integer expressions [-Wsign-compare]
), karenai
int yang ditandatangani. Untuk menghindari ini, Anda perlu menulis misalnyaatau mungkin lebih baik:
sumber
size
pengembaliansize_t
, Anda harus dapat memiliki hurufsize_t
literal0z
. Tetapi Anda dapat mendeklarasikan UDL untuk melakukan ini. (size_t operator""_z(...)
)unsigned
sangat mungkin tidak akan cukup besar untuk menampung semua nilaistd::size_t
pada arsitektur arus utama, sehingga jika seseorang memiliki wadah dengan jumlah elemen yang sangat besar, penggunaanunsigned
dapat menyebabkan loop tak terbatas pada rentang yang lebih rendah. indeks. Meskipun ini tidak mungkin menjadi masalah,std::size_t
harus digunakan untuk mendapatkan kode bersih yang maksud sinyal dengan benar. Saya tidak yakin bahkanunsigned long long
dijamin cukup, walaupun dalam praktiknya mungkin harus sama.unsigned long long
dijamin setidaknya 64 bit, tetapi secara teorisize_t
bisa lebih besar dari ini, saya kira. Tentu saja jika Anda memiliki> 2 ^ 64 elemen dalam wadah Anda, maka Anda mungkin memiliki masalah yang lebih besar untuk dikhawatirkan ... ;-)Saya pikir
auto
bagus ketika digunakan dalam konteks lokal, di mana pembaca dengan mudah & jelas dapat mengurangi jenisnya, atau didokumentasikan dengan baik dengan komentar dari jenisnya atau nama yang menyimpulkan jenis yang sebenarnya. Mereka yang tidak mengerti cara kerjanya mungkin mengambilnya dengan cara yang salah, seperti menggunakannya sebagai gantinyatemplate
atau serupa. Berikut adalah beberapa kasus penggunaan yang baik dan buruk menurut saya.Penggunaan yang baik
Iterator
Pointer fungsi
Penggunaan yang buruk
Aliran data
Fungsi Tanda Tangan
Kasus-kasus sepele
sumber
int
, Anda harus mengetikkan satu lagi arang jika mauauto
. Itu tidak bisa diterimaint
mudah dilihat di sini & mengetikint
lebih pendek. Itu sebabnya ini adalah kasus sepele.Saya terkejut tidak ada yang menyebutkan ini, tetapi anggap Anda menghitung faktorial dari sesuatu:
Kode ini akan menampilkan ini:
Itu jelas bukan hasil yang diharapkan. Itu terjadi karena
auto
menyimpulkan jenis faktorial variabelint
karena ditugaskan1
.sumber