Pertanyaan Mengapa saya harus secara eksplisit menulis kata kunci 'otomatis'?


Saya bergerak menuju C ++ 11 dari C ++ 98 dan telah menjadi terbiasa dengan auto kata kunci. Saya bertanya-tanya mengapa kita perlu secara eksplisit menyatakan auto jika compiler dapat secara otomatis menyimpulkan jenisnya. Saya tahu C ++ adalah bahasa yang sangat diketik dan ini adalah aturan tetapi tidak mungkin untuk mencapai hasil yang sama tanpa secara eksplisit mendeklarasikan variabel auto?


75
2018-05-23 08:15


asal


Jawaban:


Menjatuhkan eksplisit auto akan memecahkan bahasa:

misalnya

int main()
{
    int n;
    {
        auto n = 0; // this shadows the outer n.
    }
}

di mana Anda dapat melihat bahwa menjatuhkan auto tidak akan bayangan luar n.


154
2018-05-23 08:16



Pertanyaan Anda memungkinkan dua interpretasi:

  • Kenapa kita perlu 'otomatis' sama sekali? Tidak bisakah kita menjatuhkannya?
  • Mengapa kami wajib menggunakan mobil? Tidak bisakah kita memilikinya secara implisit, jika tidak diberikan?

Batsyeba dijawab baik interpretasi pertama, untuk yang kedua, pertimbangkan yang berikut (dengan asumsi tidak ada deklarasi lain yang ada sejauh ini; hipotetis C ++ valid):

int f();
double g();

n = f(); // declares a new variable, type is int;
d = g(); // another new variable, type is double

if(n == d)
{
    n = 7; // reassigns n
    auto d = 2.0; // new d, shadowing the outer one
}

Saya t akan menjadi mungkin, bahasa lain cukup baik dengan (baik, terlepas dari masalah bayangan mungkin) ... Ini tidak begitu dalam C + +, meskipun, dan pertanyaan (dalam arti penafsiran kedua) sekarang adalah: Mengapa?

Kali ini, jawabannya tidak sejelas interpretasi pertama. Satu hal yang jelas, meskipun: Persyaratan eksplisit untuk kata kunci membuat bahasa lebih aman (saya tidak tahu apakah ini yang mendorong komite bahasa untuk keputusannya, masih tetap menjadi titik):

grummel = f();

// ...

if(true)
{
    brummel = f();
  //^ uh, oh, a typo...
}

Bisakah kita setuju ini tidak perlu penjelasan lebih lanjut?

Bahaya yang lebih besar dalam tidak memerlukan auto, [bagaimanapun], adalah bahwa itu berarti bahwa menambahkan variabel global di tempat yang jauh dari fungsi (misalnya di file header) dapat mengubah apa yang dimaksudkan untuk menjadi deklarasi lokal- variabel scoped dalam fungsi itu menjadi penugasan ke variabel global ... dengan konsekuensi yang berpotensi menimbulkan bencana (dan tentunya sangat membingungkan).

(dikutip psmears ' komentar karena pentingnya - terima kasih atas petunjuknya)


37
2018-05-23 08:47



apakah tidak mungkin untuk mencapai hasil yang sama tanpa secara eksplisit mendeklarasikan variabel auto?

Saya akan mengulang pertanyaan Anda sedikit dengan cara yang akan membantu Anda memahami mengapa Anda membutuhkannya auto:

Apakah tidak mungkin untuk mencapai hasil yang sama tanpa secara eksplisit menggunakan placeholder tipe?

Apakah tidak mungkin? Tentu saja itu "mungkin". Pertanyaannya adalah apakah itu akan sepadan dengan upaya untuk melakukannya.

Kebanyakan sintaksis dalam bahasa lain yang tidak mengetik nama berfungsi dalam salah satu dari dua cara. Ada cara Go-like, di mana name := value; mendeklarasikan variabel. Dan ada cara seperti Python, di mana name = value; mendeklarasikan variabel baru jika name belum pernah dinyatakan sebelumnya.

Mari berasumsi bahwa tidak ada masalah sintaksis dengan menerapkan sintaks ke C ++ (meskipun saya sudah bisa melihatnya identifier diikuti oleh :di C ++ berarti "membuat label"). Jadi, apa yang Anda kalah dibandingkan dengan placeholder?

Yah, aku tidak bisa lagi melakukan ini:

auto &name = get<0>(some_tuple);

Lihat, auto selalu berarti "nilai". Jika Anda ingin mendapatkan referensi, Anda perlu secara eksplisit menggunakan &. Dan itu benar akan gagal untuk mengkompilasi jika ekspresi tugas adalah prvalue. Baik sintaks berbasis penugasan memiliki cara untuk membedakan antara referensi dan nilai.

Sekarang, Anda dapat membuat sintaks tugas tersebut menyimpulkan referensi jika nilai yang diberikan adalah referensi. Tetapi itu berarti Anda tidak bisa melakukannya:

auto name = get<0>(some_tuple);

Ini salinan dari tupel, membuat objek tidak bergantung some_tuple. Terkadang, itulah yang Anda inginkan. Ini bahkan lebih berguna jika Anda ingin berpindah dari tuple dengan auto name = get<0>(std::move(some_tuple));.

OK, jadi mungkin kita bisa memperluas sintaks ini sedikit untuk menjelaskan perbedaan ini. Mungkin &name := value; atau &name = value; akan berarti menyimpulkan referensi seperti auto&.

Baiklah. Bagaimana dengan ini:

decltype(auto) name = some_thing();

Oh itu benar; C ++ sebenarnya memiliki dua placeholder: auto dan decltype(auto). Ide dasar dari deduksi ini adalah bahwa ia bekerja persis seperti yang Anda lakukan decltype(expr) name = expr;. Jadi dalam kasus kami, jika some_thing() adalah objek, itu akan menyimpulkan objek. Jika some_thing() adalah referensi, itu akan menyimpulkan referensi.

Ini sangat berguna ketika Anda bekerja dalam kode template dan tidak yakin apa nilai balik dari suatu fungsi nantinya. Ini bagus untuk meneruskan, dan ini adalah alat penting, meskipun tidak digunakan secara luas.

Jadi sekarang kita perlu menambahkan lebih banyak ke sintaks kami. name ::= value; berarti "lakukan apa decltype(auto) tidak ". Saya tidak memiliki padanan untuk varian Pythonic.

Melihat sintaks ini, bukankah itu agak mudah salah ketik? Tidak hanya itu, hampir tidak bisa didokumentasikan sendiri. Bahkan jika Anda belum pernah melihatnya decltype(auto) sebelumnya, cukup besar dan cukup jelas bahwa Anda setidaknya dapat dengan mudah mengatakan bahwa ada sesuatu yang istimewa terjadi. Sedangkan perbedaan visual antara ::= dan := minimal.

Tapi itu hal-hal opini; ada masalah yang lebih substantif. Lihat, semua ini didasarkan pada penggunaan sintaks tugas. Nah ... bagaimana dengan tempat-tempat di mana Anda tidak bisa menggunakan sintaks tugas? Seperti ini:

for(auto &x : container)

Apakah kita mengubahnya for(&x := container)? Karena itu sepertinya mengatakan sesuatu sangat berbeda dari jangkauan berbasis for. Sepertinya itu pernyataan penginisialisasi dari reguler for loop, bukan berbasis rentang for. Ini juga akan menjadi sintaks yang berbeda dari kasus yang tidak disimpulkan.

Juga, copy-inisialisasi (menggunakan =) bukanlah hal yang sama dalam C ++ sebagai inisialisasi langsung (menggunakan sintaks konstruktor). Begitu name := value; mungkin tidak berfungsi dalam kasus di mana auto name(value) akan memiliki.

Tentu, Anda bisa menyatakan itu := akan menggunakan inisialisasi langsung, tetapi itu akan sangat selaras dengan cara sisa C ++ berperilaku.

Juga, ada satu hal lagi: C ++ 14. Ini memberi kami satu fitur deduksi yang berguna: pengurangan tipe pengembalian. Tapi ini berdasarkan placeholder. Sama seperti rentang berbasis for, secara fundamental didasarkan pada nama-nama yang diisi oleh kompiler, bukan oleh beberapa sintaks yang diterapkan pada nama dan ekspresi tertentu.

Lihat, semua masalah ini berasal dari sumber yang sama: Anda menciptakan sintaks yang sama sekali baru untuk mendeklarasikan variabel. Deklarasi berbasis Placeholder tidak harus menciptakan baru sintaksis. Mereka menggunakan sintaks yang sama persis seperti sebelumnya; mereka hanya menggunakan kata kunci baru yang bertindak seperti tipe, tetapi memiliki arti khusus. Inilah yang memungkinkannya bekerja dalam jangkauan berbasis for dan untuk pengurangan tipe kembali. Itulah yang memungkinkannya memiliki banyak bentuk (auto vs. decltype(auto)). Dan seterusnya.

Placeholder bekerja karena mereka adalah solusi paling sederhana untuk masalah ini, sementara secara bersamaan mempertahankan semua manfaat dan keumuman menggunakan nama jenis yang sebenarnya. Jika Anda menemukan alternatif lain yang berfungsi secara universal seperti yang dilakukan placeholder, sangat tidak mungkin itu akan sesederhana placeholder.

Kecuali itu hanya spelling placeholder dengan kata kunci atau simbol yang berbeda ...


14
2018-05-24 02:32



Pendeknya: auto bisa dijatuhkan dalam beberapa kasus, tetapi itu akan menyebabkan inkonsistensi.

Pertama-tama, seperti yang ditunjukkan, sintaks pernyataan dalam C ++ adalah <type> <varname>. Deklarasi eksplisit membutuhkan beberapa jenis atau setidaknya kata kunci deklarasi di tempatnya. Jadi kita bisa menggunakannya var <varname> atau declare <varname> atau sesuatu, tapi auto adalah kata kunci yang bertahan lama di C ++, dan merupakan kandidat yang baik untuk kata kunci pengurangan tipe otomatis.

Mungkinkah secara implisit mendeklarasikan variabel dengan penugasan tanpa melanggar segalanya?

Terkadang ya. Anda tidak dapat melakukan tugas di luar fungsi, sehingga Anda bisa menggunakan sintaks tugas untuk deklarasi di sana. Tetapi pendekatan semacam itu akan membawa ketidakkonsistenan pada bahasa, kemungkinan menyebabkan kesalahan manusia.

a = 0; // Error. Could be parsed as auto declaration instead.
int main() {
  return 0;
}

Dan ketika datang ke jenis deklarasi eksplisit variabel lokal mereka cara mengendalikan ruang lingkup variabel.

a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here

Jika sintaks rancu dibolehkan, mendeklarasikan variabel global dapat secara tiba-tiba mengubah deklarasi implisit lokal ke penugasan. Menemukan konversi tersebut akan membutuhkan pemeriksaan segala sesuatu. Dan untuk menghindari tabrakan, Anda akan membutuhkan nama-nama unik untuk semua global, yang akan menghancurkan seluruh gagasan pelingkupan. Jadi ini sangat buruk.


12
2018-05-23 13:55



auto adalah kata kunci yang dapat Anda gunakan di tempat-tempat di mana Anda biasanya perlu menentukan mengetik.

  int x = some_function();

Dapat dibuat lebih generik dengan membuat int ketik otomatis disimpulkan:

  auto x = some_function();

Jadi itu adalah perpanjangan konservatif terhadap bahasa; itu cocok dengan sintaks yang ada. Tanpa itu x = some_function() menjadi pernyataan penugasan, bukan lagi sebuah deklarasi.


11
2018-05-23 08:28



sintaks harus tidak ambigu dan juga kompatibel ke belakang.

Jika mobil dijatuhkan tidak akan ada cara untuk membedakan antara pernyataan dan definisi.

auto n = 0; // fine
n=0; // statememt, n is undefined.

9
2018-05-23 08:24



Menambah jawaban sebelumnya, satu catatan tambahan dari kentut tua: Sepertinya Anda melihatnya sebagai keuntungan untuk dapat mulai menggunakan variabel baru tanpa mendeklarasikannya.

Dalam bahasa dengan kemungkinan definisi implisit variabel ini bisa menjadi masalah besar, terutama dalam sistem yang lebih besar. Anda membuat satu kesalahan ketik dan Anda men-debug selama berjam-jam hanya untuk mengetahui Anda secara tidak sengaja memperkenalkan variabel dengan nilai nol (atau lebih buruk) - blue vs bleu, label vs lable ... hasilnya adalah Anda tidak dapat benar-benar mempercayai kode apa pun tanpa memeriksa nama variabel secara tepat.

Hanya menggunakan auto memberi tahu kedua kompilator dan pengelola bahwa itu adalah niat Anda untuk mendeklarasikan variabel baru.

Pikirkanlah, untuk dapat menghindari mimpi buruk semacam ini pernyataan 'tidak ada implisit' diperkenalkan di FORTRAN - dan Anda melihatnya digunakan di semua program FORTRAN yang serius saat ini. Tidak memilikinya hanya ... menakutkan.


3
2018-05-30 00:03