Apakah otomatis membuat kode C ++ lebih sulit untuk dipahami?

122

Saya melihat sebuah konferensi oleh Herb Sutter di mana ia mendorong setiap programmer C ++ untuk menggunakannya auto.

Saya harus membaca kode C # beberapa waktu yang lalu di mana vardigunakan secara luas dan kode itu sangat sulit dipahami — setiap kali vardigunakan saya harus memeriksa kembali tipe sisi kanan. Terkadang lebih dari sekali, karena saya lupa jenis variabel setelah beberapa saat!

Saya tahu kompiler tahu jenisnya dan saya tidak harus menulisnya, tetapi secara luas diterima bahwa kita harus menulis kode untuk programmer, bukan untuk kompiler.

Saya juga tahu itu lebih mudah untuk ditulis:

auto x = GetX();

Dari:

someWeirdTemplate<someOtherVeryLongNameType, ...>::someOtherLongType x = GetX();

Tapi ini ditulis hanya sekali dan GetX()tipe kembalinya diperiksa berkali-kali untuk memahami tipe apa yang xdimiliki.

Ini membuat saya bertanya-tanya - apakah automembuat kode C ++ lebih sulit untuk dipahami?

Mircea Ispas
sumber
29
Apakah Anda benar-benar perlu memeriksa jenis pengembalian setiap waktu ? Mengapa jenis ini tidak jelas dari kode? autosering dapat membuat hal-hal lebih sulit untuk dibaca ketika mereka sudah sulit dibaca, yaitu, fungsi terlalu lama, variabel nama buruk, dll. Pada fungsi pendek dengan variabel nama yang layak, mengetahui jenis harus salah satu dari # 1 mudah atau # 2 tidak relevan.
R. Martinho Fernandes
25
"Seni" menggunakan autosangat mirip menentukan kapan harus digunakan typedef. Terserah Anda untuk menentukan kapan itu menghalangi dan kapan itu membantu.
ahenderson
18
Saya pikir saya memiliki masalah yang sama, tetapi kemudian saya menyadari bahwa saya hanya dapat memahami kode tanpa mengetahui tipenya. mis: "auto idx = get_index ();" jadi idx adalah sesuatu yang menyimpan indeks. Apa jenis pastinya, sangat tidak relevan untuk kebanyakan kasus.
PlasmaHH
31
Jadi jangan menulis auto x = GetX();, pilih nama yang lebih baik daripada xyang benar-benar memberi tahu Anda apa yang dilakukannya dalam konteks tertentu ... itu sering lebih berguna daripada tipenya.
Jonathan Wakely
11
Jika menggunakan lebih banyak inferensi tipe menyulitkan programmer untuk membaca kode, baik kode atau programmer perlu perbaikan serius.
CA McCann

Jawaban:

100

Jawaban singkat: Secara lebih lengkap, pendapat saya saat ini autoadalah bahwa Anda harus menggunakan autosecara default kecuali Anda secara eksplisit menginginkan konversi. (Sedikit lebih tepatnya, "... kecuali jika Anda ingin secara eksplisit berkomitmen pada suatu jenis, yang hampir selalu karena Anda menginginkan konversi.")

Jawaban dan alasan yang lebih panjang:

Tulis jenis yang eksplisit (bukan auto) hanya ketika Anda benar-benar ingin berkomitmen secara eksplisit untuk suatu jenis, yang hampir selalu berarti Anda ingin secara eksplisit mendapatkan konversi ke jenis itu. Dari atas kepala saya, saya ingat dua kasus utama:

  • (Biasa) initializer_listKejutan yang auto x = { 1 };menyimpulkan initializer_list. Jika Anda tidak mau initializer_list, ucapkan tipe - mis., Secara eksplisit minta konversi.
  • (Rare) Kasus templat ekspresi, seperti yang auto x = matrix1 * matrix 2 + matrix3;menangkap jenis pembantu atau proxy yang tidak dimaksudkan untuk terlihat oleh programmer. Dalam banyak kasus, baik-baik saja dan jinak untuk menangkap jenis itu, tetapi kadang-kadang jika Anda benar-benar ingin itu runtuh dan melakukan perhitungan kemudian katakan jenisnya - yaitu, sekali lagi secara eksplisit meminta konversi.

autoSebaliknya, gunakan standar secara rutin , karena automenghindari jebakan dan membuat kode Anda lebih benar, lebih mudah dirawat, kuat, dan lebih efisien. Secara kasar, dari yang paling penting hingga yang paling tidak penting, dalam semangat "menulis untuk kejelasan dan kebenaran terlebih dahulu":

  • Kebenaran: Menggunakan autojaminan Anda akan mendapatkan jenis yang tepat. Seperti kata pepatah, jika Anda mengulangi diri sendiri (ucapkan tipenya secara berlebihan), Anda bisa dan akan berbohong (salah). Berikut ini adalah contoh yang biasa: void f( const vector<int>& v ) { for( /*…*- pada titik ini, jika Anda menulis jenis iterator secara eksplisit, Anda ingin mengingat untuk menulis const_iterator(bukan?), Sedangkan yang autobenar saja.
  • Maintainability dan robustness: Menggunakan automembuat kode Anda lebih kuat dalam menghadapi perubahan, karena ketika jenis ekspresi berubah, autoakan terus menyelesaikan ke jenis yang benar. Jika Anda lebih memilih komit ke tipe eksplisit, mengubah tipe ekspresi akan menyuntikkan konversi diam ketika tipe baru dikonversi ke tipe lama, atau build tidak perlu pecah ketika tipe baru masih berfungsi - seperti tipe lama tetapi tidak dikonversi ke yang lama ketik (misalnya, ketika Anda mengubah mapke unordered_map, yang selalu baik-baik saja jika Anda tidak bergantung pada pesanan, menggunakan autountuk iterator Anda Anda akan beralih dari map<>::iteratorke unordered_map<>::iterator, tetapi menggunakanmap<>::iterator di mana-mana secara eksplisit berarti Anda akan membuang-buang waktu berharga Anda pada riak memperbaiki kode mekanik, kecuali jika magang berjalan dan Anda dapat mengesampingkan pekerjaan yang membosankan pada mereka).
  • Kinerja: Karena autotidak ada jaminan konversi implisit akan terjadi, itu menjamin kinerja yang lebih baik secara default. Jika sebaliknya Anda mengatakan jenisnya, dan itu membutuhkan konversi, Anda akan sering mendapatkan konversi diam-diam apakah Anda mengharapkannya atau tidak.
  • Kegunaan: Menggunakan autoadalah satu-satunya pilihan Anda yang baik untuk jenis yang sulit untuk mengeja dan tidak dapat dihancurkan, seperti lambda dan bantuan templat, singkat menggunakan decltypeekspresi berulang atau tipuan kurang efisien seperti std::function.
  • Kenyamanan: Dan, ya, autokurang mengetik. Saya menyebutkan bahwa terakhir untuk kelengkapan karena itu adalah alasan umum untuk menyukainya, tetapi itu bukan alasan terbesar untuk menggunakannya.

Oleh karena itu: Lebih suka mengatakannya autosecara default. Ini menawarkan begitu banyak kesederhanaan dan kinerja serta kejernihan yang baik sehingga Anda hanya menyakiti diri sendiri (dan pengelola kode Anda di masa mendatang) jika tidak. Hanya berkomitmen untuk jenis eksplisit ketika Anda benar-benar bersungguh-sungguh, yang hampir selalu berarti Anda menginginkan konversi eksplisit.

Ya, ada (sekarang) mengerti tentang hal ini.

Sutera Ramuan
sumber
14
Saya menemukan otomatis berguna bahkan ketika saya ingin konversi. Hal ini memungkinkan saya untuk secara eksplisit meminta konversi tanpa mengulangi jenis: auto x = static_cast<X>(y). The static_castmemperjelas bahwa konversi itu sengaja dan menghindari peringatan kompilator tentang konversi. Biasanya menghindari peringatan kompiler tidak begitu baik, tapi saya baik-baik saja dengan tidak mendapatkan peringatan tentang konversi yang saya pertimbangkan dengan hati-hati ketika saya menulis static_cast. Meskipun saya tidak akan melakukan ini jika tidak ada peringatan sekarang tapi saya ingin mendapatkan peringatan di masa depan jika jenisnya berubah dengan cara yang berpotensi berbahaya.
Bjarke Hammersholt Roune
6
Satu hal yang saya temukan autoadalah bahwa kita harus berusaha untuk memprogram terhadap antarmuka (bukan dalam arti OOP), bukan terhadap implementasi spesifik. Sama dengan template, sungguh. Apakah Anda mengeluh tentang "kode sulit dibaca" karena Anda memiliki parameter tipe template Tyang digunakan di mana-mana? Tidak, kurasa tidak. Dalam templat juga kami kode terhadap antarmuka, kompilasi waktu bebek-mengetik adalah apa yang banyak orang menyebutnya.
Xeo
6
"Menggunakan jaminan otomatis, kamu akan mendapatkan tipe yang tepat." Tidak benar sama sekali. Ini hanya menjamin Anda akan mendapatkan jenis yang ditentukan oleh bagian lain dari kode Anda. Benar atau tidak sepenuhnya tidak jelas ketika Anda menyembunyikannya auto.
Lightness Races dalam Orbit
Saya benar-benar terkejut bahwa tidak ada yang peduli dengan IDE ... Bahkan IDE modern tidak dengan benar mendukung lompatan ke definisi kelas / struct dalam hal autovariabel, tetapi hampir semua dari mereka melakukannya dengan benar dengan spesifikasi tipe eksplisit. Tidak ada yang menggunakan IDE? Semua orang hanya menggunakan variabel int / float / bool? Semua orang lebih suka dokumentasi eksternal untuk perpustakaan daripada header yang didokumentasikan sendiri?
avtomaton
bahwa GotW: herbalutter.com/2013/08/12/... Saya tidak melihat bagaimana "kejutan initializer_list" itu mengejutkan; kawat gigi di sekitar =RHS tidak masuk akal dengan interpretasi lain (bar brit init-list, tetapi Anda perlu tahu apa yang Anda inisialisasi, yang merupakan sebuah oxymoron dengan auto). Salah satu yang adalah mengejutkan adalah auto i{1}juga menyimpulkan initializer_list, meskipun menyiratkan tidak mengambil ini bersiap init-daftar melainkan mengambil ungkapan ini dan menggunakan jenisnya ... tapi kita initializer_listdi sana juga. Untungnya, C ++ 17 memperbaiki semua ini dengan baik.
underscore_d
112

Ini adalah situasi per kasus.

Terkadang membuat kode lebih sulit untuk dipahami, terkadang tidak. Ambil, misalnya:

void foo(const std::map<int, std::string>& x)
{
   for ( auto it = x.begin() ; it != x.end() ; it++ )
   { 
       //....
   }
}

jelas mudah dimengerti dan jelas lebih mudah untuk ditulis daripada deklarasi iterator yang sebenarnya.

Saya telah menggunakan C ++ untuk sementara waktu sekarang, tetapi saya dapat menjamin bahwa saya akan mendapatkan kesalahan kompiler pada kesempatan pertama saya karena saya akan melupakan const_iteratordan awalnya akan pergi untuk iterator... :)

Saya akan menggunakannya untuk kasus-kasus seperti ini, tetapi tidak di mana itu benar-benar mengaburkan jenis (seperti situasi Anda), tetapi ini murni subjektif.

Luchian Grigore
sumber
45
Persis. Siapa sih yang peduli dengan tipe tersebut. Itu adalah iterator. Saya tidak peduli dengan jenisnya, yang perlu saya ketahui adalah saya bisa menggunakannya untuk beralih.
R. Martinho Fernandes
5
+1. Bahkan jika Anda menyebutkan nama jenisnya, Anda akan menamainya std::map<int, std::string>::const_iterator, jadi itu bukan seolah-olah namanya memberi tahu Anda banyak tentang jenis itu.
Steve Jessop
4
@SteveJessop: Ini memberitahu saya dua hal setidaknya: kuncinya int, dan nilainya std::string. :)
Nawaz
16
@Nawaz: dan Anda tidak dapat menetapkan it->secondkarena itu adalah konstelasi. Semua informasi yang merupakan pengulangan dari apa yang ada di baris sebelumnya const std::map<int, std::string>& x,. Mengatakan sesuatu berkali-kali kadang-kadang memberi informasi yang lebih baik, tetapi tidak berarti bahwa aturan umum :-)
Steve Jessop
11
TBH Saya lebih suka for (anX : x)untuk membuatnya lebih jelas kita hanya mengulanginya x. Kasus normal di mana Anda perlu sebuah iterator adalah ketika Anda memodifikasi kontainer, tetapi xadalahconst&
MSalters
94

Lihatlah dengan cara lain. Apakah Anda menulis:

std::cout << (foo() + bar()) << "\n";

atau:

// it is important to know the types of these values
int f = foo();
size_t b = bar();
size_t total = f + b;

std::cout << total << "\n";

Terkadang tidak membantu untuk mengeja jenis ini secara eksplisit.

Keputusan apakah Anda perlu menyebutkan jenis tidak sama dengan keputusan apakah Anda ingin membagi kode di beberapa pernyataan dengan mendefinisikan variabel perantara. Di C ++ 03 keduanya terhubung, Anda dapat menganggapnya autosebagai cara untuk memisahkan mereka.

Kadang-kadang membuat tipe eksplisit dapat berguna:

// seems legit    
if (foo() < bar()) { ... }

vs.

// ah, there's something tricky going on here, a mixed comparison
if ((unsigned int)foo() < bar()) { ... }

Dalam kasus di mana Anda mendeklarasikan variabel, menggunakan automembiarkan tipe tidak terucapkan seperti halnya dalam banyak ekspresi. Anda mungkin harus mencoba memutuskan sendiri kapan hal itu membantu keterbacaan dan kapan hal itu menghalangi.

Anda dapat berargumen bahwa memadukan tipe yang bertanda tangan dan tidak bertanda merupakan kesalahan sejak awal (memang, beberapa orang berpendapat lebih jauh bahwa seseorang tidak boleh menggunakan jenis yang tidak bertanda sama sekali). The Alasan itu bisa dibilang kesalahan adalah membuat jenis operan sangat penting karena perilaku yang berbeda. Jika Anda perlu mengetahui jenis-jenis nilai Anda, itu mungkin juga bukan hal buruk untuk tidak perlu mengetahuinya. Jadi asalkan kode tersebut belum membingungkan karena alasan lain, itu tidak autoapa-apa, kan? ;-)

Khususnya ketika menulis kode generik ada kasus di mana jenis sebenarnya dari variabel tidak boleh penting, yang penting adalah bahwa itu memenuhi antarmuka yang diperlukan. Jadi autoberikan tingkat abstraksi di mana Anda mengabaikan jenisnya (tapi tentu saja kompiler tidak, ia tahu). Bekerja pada level abstraksi yang sesuai dapat membantu keterbacaan cukup banyak, bekerja pada level "salah" membuat pembacaan kode menjadi pekerjaan berat.

Steve Jessop
sumber
21
+1 automemungkinkan Anda membuat variabel bernama dengan tipe yang tidak dapat disebutkan namanya atau tidak menarik. Nama yang bermakna dapat bermanfaat.
Mankarse
Mencampur ditandatangani dan tidak ditandatangani jika Anda menggunakan unsigned untuk penggunaan yang tepat: aritmatika modular. Bukan jika Anda menyalahgunakan unsigned untuk bilangan bulat positif. Hampir tidak ada program yang digunakan untuk yang tidak ditandatangani, tetapi bahasa inti memaksakan definisi yang tidak masuk akal sizeofsebagai tidak ditandatangani pada Anda.
curiousguy
27

IMO, Anda melihat ini secara terbalik.

Bukan masalah automengarah ke kode yang tidak dapat dibaca atau bahkan kurang dapat dibaca. Ini masalah (berharap bahwa) memiliki tipe eksplisit untuk nilai pengembalian akan menggantikan fakta bahwa (tampaknya) tidak jelas tipe apa yang akan dikembalikan oleh beberapa fungsi tertentu.

Setidaknya menurut saya, jika Anda memiliki fungsi yang tipe pengembaliannya tidak segera jelas, itu masalah Anda di sana. Apa fungsi itu harus jelas dari namanya, dan jenis nilai pengembalian harus jelas dari apa yang dilakukannya. Jika tidak, itulah sumber masalahnya.

Jika ada masalah di sini, bukan dengan auto. Ini dengan sisa kode, dan kemungkinan cukup bagus bahwa jenis eksplisit hanya cukup band-bantuan untuk membuat Anda melihat dan / atau memperbaiki masalah inti. Setelah Anda memperbaiki masalah nyata itu, keterbacaan kode menggunakan autoumumnya akan baik-baik saja.

Saya kira secara adil saya harus menambahkan: Saya sudah berurusan dengan beberapa kasus di mana hal-hal seperti itu tidak sejelas yang Anda inginkan, dan memperbaiki masalah juga tidak bisa dipertahankan. Sebagai contoh, saya melakukan konsultasi untuk sebuah perusahaan beberapa tahun yang lalu yang sebelumnya bergabung dengan perusahaan lain. Mereka berakhir dengan basis kode yang lebih "didorong bersama" daripada benar-benar bergabung. Program konstituen sudah mulai menggunakan perpustakaan yang berbeda (tapi sangat mirip) untuk tujuan yang sama, dan meskipun mereka bekerja untuk menggabungkan hal-hal yang lebih bersih, mereka masih melakukannya. Dalam banyak kasus, satu-satunya cara untuk menebak jenis apa yang akan dikembalikan oleh fungsi yang diberikan adalah untuk mengetahui dari mana fungsi itu berasal.

Bahkan dalam kasus seperti itu, Anda dapat membantu membuat beberapa hal menjadi lebih jelas. Dalam hal ini, semua kode dimulai di namespace global. Cukup memindahkan jumlah yang wajar ke beberapa ruang nama menghilangkan bentrokan nama dan sedikit memudahkan pelacakan tipe.

Jerry Coffin
sumber
17

Ada beberapa alasan mengapa saya tidak suka otomatis untuk penggunaan umum:

  1. Anda dapat memperbaiki kode tanpa mengubahnya. Ya, ini adalah salah satu hal yang sering didaftar sebagai manfaat menggunakan otomatis. Ubah saja tipe fungsi yang dikembalikan, dan jika semua kode yang memanggilnya menggunakan otomatis, tidak diperlukan upaya tambahan! Anda menekan kompilasi, itu membangun - 0 peringatan, 0 kesalahan - dan Anda hanya melanjutkan dan memeriksa kode Anda tanpa harus berurusan dengan kekacauan melihat-lihat dan berpotensi memodifikasi 80 tempat fungsi digunakan.

Tapi tunggu, apakah itu ide yang bagus? Bagaimana jika jenisnya penting dalam setengah lusin kasus penggunaan tersebut, dan sekarang kode itu benar-benar berperilaku berbeda? Ini juga secara implisit dapat memecahkan enkapsulasi, dengan memodifikasi tidak hanya nilai input, tetapi perilaku itu sendiri dari implementasi privat dari kelas lain yang memanggil fungsi.

1a. Saya seorang yang percaya pada konsep "kode dokumentasi diri". Alasan di balik kode dokumentasi diri adalah bahwa komentar cenderung menjadi ketinggalan zaman, tidak lagi mencerminkan apa yang dilakukan kode, sedangkan kode itu sendiri - jika ditulis secara eksplisit - jelas, selalu tetap up to date. pada niatnya, dan tidak akan membuat Anda bingung dengan komentar basi. Jika tipe dapat diubah tanpa perlu memodifikasi kode itu sendiri, maka kode / variabel itu sendiri dapat menjadi basi. Sebagai contoh:

auto bThreadOK = CheckThreadHealth ();

Kecuali masalahnya adalah bahwa CheckThreadHealth () di beberapa titik telah di refactored untuk mengembalikan nilai enum yang menunjukkan status kesalahan, jika ada, bukan bool. Tetapi orang yang melakukan perubahan itu gagal memeriksa baris kode khusus ini, dan kompiler tidak membantu karena dikompilasi tanpa peringatan atau kesalahan.

  1. Anda mungkin tidak pernah tahu apa tipe sebenarnya. Ini juga sering terdaftar sebagai "manfaat" utama otomatis. Mengapa belajar apa fungsi memberi Anda, ketika Anda bisa mengatakan, "Siapa yang peduli? Itu mengkompilasi!"

Bahkan jenis pekerjaan, mungkin. Saya mengatakan jenis pekerjaan, karena meskipun Anda membuat salinan struct 500 byte untuk setiap iterasi loop, sehingga Anda dapat memeriksa nilai tunggal di dalamnya, kode masih berfungsi sepenuhnya. Jadi, bahkan pengujian unit Anda tidak membantu Anda menyadari bahwa kode buruk bersembunyi di balik otomatis yang sederhana dan tampak tidak bersalah itu. Kebanyakan orang lain memindai file tidak akan melihatnya pada pandangan pertama juga.

Ini juga dapat menjadi lebih buruk jika Anda tidak tahu apa jenisnya, tetapi Anda memilih nama variabel yang membuat asumsi yang salah tentang apa itu, pada dasarnya mencapai hasil yang sama seperti pada 1a, tetapi sejak awal bukan pasca-refactor.

  1. Mengetik kode ketika awalnya menulis itu bukan bagian yang paling memakan waktu dari pemrograman. Ya, otomatis membuat penulisan beberapa kode lebih cepat pada awalnya. Sebagai penafian, saya mengetik> 100 WPM, jadi mungkin itu tidak mengganggu saya sebanyak yang lain. Tetapi jika yang harus saya lakukan adalah menulis kode baru sepanjang hari, saya akan menjadi kemping yang bahagia. Bagian pemrograman yang paling banyak menghabiskan waktu adalah mendiagnosis bug tepi-kode yang sulit direproduksi, sering kali merupakan hasil dari masalah tidak jelas yang tidak jelas - seperti penggunaan mobil yang terlalu sering digunakan (referensi vs. salinan, ditandatangani vs. unsigned, float vs. int, bool vs. pointer, dll.).

Tampak jelas bagi saya bahwa otomatis diperkenalkan terutama sebagai solusi untuk sintaks yang mengerikan dengan tipe templat perpustakaan standar. Alih-alih mencoba memperbaiki sintaks templat yang sudah dikenal banyak orang - yang mungkin juga hampir mustahil dilakukan karena semua kode yang ada dapat rusak - tambahkan kata kunci yang pada dasarnya menyembunyikan masalah. Pada dasarnya apa yang Anda sebut "hack".

Saya sebenarnya tidak memiliki ketidaksetujuan dengan penggunaan otomatis dengan kontainer perpustakaan standar. Ini jelas untuk apa kata kunci itu dibuat, dan fungsi-fungsi di perpustakaan standar tidak mungkin secara mendasar mengubah tujuan (atau tipe dalam hal ini), membuat otomatis relatif aman untuk digunakan. Tetapi saya akan sangat berhati-hati dalam menggunakannya dengan kode dan antarmuka Anda sendiri yang mungkin jauh lebih tidak stabil, dan berpotensi mengalami perubahan yang lebih mendasar.

Aplikasi lain yang berguna dari auto yang meningkatkan kemampuan bahasa adalah membuat temporaries dalam macro type-agnostic. Ini adalah sesuatu yang tidak bisa Anda lakukan sebelumnya, tetapi Anda bisa melakukannya sekarang.

Tim W
sumber
4
Kamu berhasil. Seandainya saya bisa memberikan ini +2.
cmaster
Jawaban yang baik "berhati-hatilah" - jawab. @ cmaster: Ini dia.
Deduplicator
Saya telah menemukan satu lagi kasus berguna: auto something = std::make_shared<TypeWithLongName<SomeParam>>(a,b,c);. :-)
Notinlist
14

Ya, akan lebih mudah untuk mengetahui jenis variabel Anda jika Anda tidak menggunakannya auto. Pertanyaannya adalah: apakah Anda perlu tahu jenis variabel Anda untuk membaca kode? Terkadang jawabannya adalah ya, kadang tidak. Misalnya, ketika mendapatkan sebuah iterator dari std::vector<int>, yang perlu Anda tahu bahwa itu adalah std::vector<int>::iteratoratau akan auto iterator = ...;cukup? Segala sesuatu yang orang ingin lakukan dengan iterator diberikan oleh fakta itu iterator - hanya saja tidak peduli apa jenisnya secara spesifik.

Gunakan autodalam situasi itu ketika itu tidak membuat kode Anda lebih sulit untuk dibaca.

Joseph Mansfield
sumber
12

Secara pribadi saya autohanya menggunakan ketika benar-benar jelas bagi programmer apa itu.

Contoh 1

std::map <KeyClass, ValueClass> m;
// ...
auto I = m.find (something); // OK, find returns an iterator, everyone knows that

Contoh 2

MyClass myObj;
auto ret = myObj.FindRecord (something)// NOT OK, everyone needs to go and check what FindRecord returns

sumber
5
Ini adalah contoh yang jelas dari penamaan yang buruk yang dapat dibaca, tidak benar-benar otomatis. Tidak ada yang memiliki ide samar apa yang "DoSomethingWeird" tidak, jadi menggunakan otomatis atau tidak tidak akan membuatnya lebih mudah dibaca. Anda harus memeriksa dokumen dengan cara apa pun.
R. Martinho Fernandes
4
Ok, sekarang agak lebih baik. Saya masih menemukan variabel yang namanya kurang baik, yang masih menyakitkan. Jika Anda menulisnya auto record = myObj.FindRecord(something)akan jelas bahwa tipe variabel itu direkam. Atau penamaan itatau sejenisnya akan membuatnya jelas mengembalikan iterator. Perhatikan bahwa, bahkan jika Anda tidak menggunakan auto, penamaan variabel dengan benar berarti Anda tidak perlu kembali ke deklarasi untuk melihat tipe dari mana saja dalam fungsi . Saya menghapus downvote saya karena contohnya bukan strawman lengkap sekarang, tapi saya masih tidak membeli argumen di sini.
R. Martinho Fernandes
2
Untuk menambahkan ke @ R.MartinhoFernandes: pertanyaannya adalah, apakah benar-benar penting sekarang APA "catatan" sebenarnya? Ini saya pikir lebih penting BAHWA itu adalah catatan, tipe primitif yang mendasari sebenarnya adalah lapisan abstraksi lain .. Jadi seseorang tanpa auto mungkin memiliki:MyClass::RecordTy record = myObj.FindRecord (something)
paul23
2
@ paul23: Apa yang menggunakan auto versus tipe memberi Anda, maka, jika satu-satunya keberatan Anda adalah "Saya tidak tahu cara menggunakan ini". Entah membuat Anda tetap melihatnya.
GManNickG
3
@ GMNickG memberitahu saya jenis yang tepat dari tidak penting.
paul23
10

Pertanyaan ini mengumpulkan opini, yang akan bervariasi dari satu programmer ke programmer lainnya, tetapi saya akan mengatakan tidak. Bahkan dalam banyak kasus justru sebaliknya, autodapat membantu membuat kode lebih mudah dipahami dengan memungkinkan programmer untuk fokus pada logika daripada hal-hal kecil.

Ini terutama benar dalam menghadapi tipe template yang kompleks. Berikut adalah contoh yang disederhanakan & dibuat-buat. Mana yang lebih mudah dipahami?

for( std::map<std::pair<Foo,Bar>, std::pair<Baz, Bot>, std::less<BazBot>>::const_iterator it = things_.begin(); it != things_.end(); ++it )

.. atau...

for( auto it = things_.begin(); it != things_.end(); ++it )

Beberapa akan mengatakan yang kedua lebih mudah dimengerti, yang lain mungkin mengatakan yang pertama. Namun yang lain mungkin mengatakan bahwa penggunaan gratis autodapat berkontribusi untuk mematikan programmer yang menggunakannya, tapi itu cerita lain.

John Dibling
sumber
4
+1 Haha, semua orang memberikan std::mapcontoh, selain itu dengan argumen templat yang kompleks.
Nawaz
1
@Nawaz: Sangat mudah untuk membuat nama templat panjang-gila menggunakan maps. :)
John Dibling
@Nawaz: tapi saya ingin tahu mengapa tidak ada yang datang dengan rentang berdasarkan loop sebagai alternatif yang lebih baik dan lebih mudah dibaca ...
PlasmaHH
1
@PlasmaHH, tidak semua loop dengan iterator dapat diganti dengan berbasis-rentang formisalnya jika iterator tidak valid dalam loop body dan karenanya perlu pra-kenaikan atau tidak bertambah sama sekali.
Jonathan Wakely
@PlasmaHH: Dalam kasus saya, MSVC10 tidak melakukan range-based untuk loop. Karena MSVC10 adalah test-to C ++ 11 saya, saya tidak punya banyak pengalaman dengan mereka.
John Dibling
8

Banyak jawaban bagus sejauh ini, tetapi untuk fokus pada pertanyaan awal, saya pikir Herb terlalu jauh dalam sarannya untuk menggunakan autosecara bebas. Contoh Anda adalah satu kasus di mana penggunaan autojelas menyakitkan keterbacaan. Beberapa orang bersikeras itu bukan masalah dengan IDE modern di mana Anda dapat mengarahkan variabel dan melihat jenisnya, tetapi saya tidak setuju: bahkan orang yang selalu menggunakan IDE terkadang perlu melihat potongan kode secara terpisah (pikirkan ulasan kode) , misalnya) dan IDE tidak akan membantu.

Intinya: gunakan autoketika itu membantu: mis. Iterators in for loop. Jangan menggunakannya saat itu membuat pembaca kesulitan untuk mengetahui jenisnya.

Nemanja Trifunovic
sumber
6

Saya cukup terkejut bahwa belum ada yang menunjukkan bahwa otomatis membantu jika tidak ada tipe yang jelas. Dalam kasus ini, Anda dapat mengatasi masalah ini dengan menggunakan #define atau typedef dalam templat untuk menemukan jenis yang sebenarnya dapat digunakan (dan ini kadang-kadang tidak sepele), atau Anda hanya menggunakan otomatis.

Misalkan Anda memiliki fungsi, yang mengembalikan sesuatu dengan tipe platform-spesifik:

#ifdef PLATFROM1
__int256 getStuff();
#else //PLATFORM2
__int128 getStuff();
#endif

Penggunaan penyihir yang Anda inginkan?

#ifdef PLATFORM1
__int256 stuff = getStuff();
#else
__int128 stuff = getStuff();
#endif

atau hanya sederhana

auto stuff = getStuff();

Tentu, Anda bisa menulis

#define StuffType (...)

juga di suatu tempat, tetapi tidak

StuffType stuff = getStuff();

sebenarnya memberi tahu lebih banyak tentang tipe x? Mengatakan itu adalah apa yang dikembalikan dari sana, tetapi itulah yang otomatis. Ini hanya mubazir - 'barang' ditulis 3 kali di sini - ini menurut saya membuatnya kurang mudah dibaca daripada versi 'otomatis'.

Lrdx
sumber
5
Cara yang benar untuk menangani tipe spesifik platform adalah bagi typedefmereka.
cmaster
3

Keterbacaan itu subyektif; Anda harus melihat situasi dan memutuskan yang terbaik.

Seperti yang Anda tunjukkan, tanpa otomatis, deklarasi panjang dapat menghasilkan banyak kekacauan. Tetapi seperti yang Anda tunjukkan, deklarasi pendek dapat menghapus informasi jenis yang mungkin berharga.

Selain itu, saya juga akan menambahkan ini: pastikan Anda melihat keterbacaan dan bukan kemampuan menulis. Kode yang mudah ditulis umumnya tidak mudah dibaca dan sebaliknya. Misalnya, jika saya menulis, saya lebih suka otomatis. Jika saya membaca, mungkin deklarasi yang lebih panjang.

Lalu ada konsistensi; seberapa pentingkah hal itu bagi Anda? Apakah Anda ingin otomatis di beberapa bagian dan deklarasi eksplisit di bagian lain, atau satu metode yang konsisten di seluruh?


sumber
2

Saya akan mengambil titik kode yang kurang dapat dibaca sebagai keuntungan, dan akan mendorong programmer untuk menggunakannya lebih dan lebih. Mengapa? Jelas jika kode menggunakan otomatis sulit dibaca, maka akan sulit juga untuk menulis. Programmer dipaksa untuk menggunakan nama variabel yang bermakna , untuk membuat pekerjaannya lebih baik.
Mungkin pada awalnya programmer tidak dapat menulis nama variabel yang bermakna. Tetapi pada akhirnya ketika memperbaiki bug, atau dalam tinjauan kode, ketika dia harus menjelaskan kode tersebut kepada orang lain, atau dalam waktu yang tidak begitu dekat, dia menjelaskan kode kepada orang pemeliharaan, programmer akan menyadari kesalahan dan akan menggunakan nama variabel yang bermakna di masa depan.

Manoj R
sumber
2
Paling-paling, Anda akan membuat orang menulis nama variabel ingin myComplexDerivedTypemengganti dengan tipe yang hilang, yang mengacaukan kode dengan pengulangan tipe (di mana pun variabel digunakan), dan yang membujuk orang untuk menghilangkan tujuan variabel dalam namanya . Pengalaman saya adalah, bahwa tidak ada yang tidak produktif selain secara aktif menempatkan hambatan dalam kode.
cmaster
2

Saya punya dua pedoman:

  • Jika jenis variabelnya jelas, membosankan untuk ditulis atau sulit untuk menentukan penggunaan otomatis.

    auto range = 10.0f; // Obvious
    
    for (auto i = collection.cbegin(); i != cbegin(); ++i) // Tedious if collection type
    // is really long
    
    template <typename T> ... T t; auto result = t.get(); // Hard to determine as get()
    // might return various stuff
  • Jika Anda memerlukan konversi tertentu atau jenis hasilnya tidak jelas dan dapat menyebabkan kebingungan.

    class B : A {}; A* foo = new B(); // 'Convert'
    
    class Factory { public: int foo(); float bar(); }; int f = foo(); // Not obvious
Merah XIII
sumber
0

Iya. Ini mengurangi verbositas tetapi kesalahpahaman yang umum adalah bahwa verbositas mengurangi keterbacaan. Ini hanya benar jika Anda menganggap keterbacaan sebagai estetika daripada kemampuan Anda yang sebenarnya untuk kode intepret - yang tidak meningkat dengan menggunakan otomatis. Dalam contoh yang paling sering dikutip, vektor iterator, itu mungkin muncul di permukaan yang menggunakan otomatis meningkatkan keterbacaan kode Anda. Di sisi lain, Anda tidak selalu tahu apa kata kunci otomatis akan memberi Anda. Anda harus mengikuti jalur logis yang sama seperti yang dilakukan oleh kompiler untuk melakukan rekonstruksi internal, dan seringkali, khususnya dengan iterator, Anda akan membuat asumsi yang salah.

Pada akhirnya, 'otomatis' mengorbankan keterbacaan kode dan kejelasan, untuk 'kebersihan' sintaksis dan estetika (yang hanya diperlukan karena iterator memiliki sintaks yang berbelit-belit tidak perlu) dan kemampuan untuk mengetik 10 karakter lebih sedikit pada setiap baris yang diberikan. Itu tidak sepadan dengan risikonya, atau upaya yang dilakukan untuk jangka panjang.

metamorfosis
sumber