Mengapa std::initializer_list
bahasa inti tidak ada di dalamnya?
Menurut saya, ini adalah fitur yang cukup penting dari C ++ 11 namun tidak memiliki kata kunci yang dicadangkan sendiri (atau yang serupa).
Sebaliknya, initializer_list
itu hanya kelas template dari pustaka standar yang memiliki pemetaan implisit khusus dari sintaks braced-init-list baru {...}
yang ditangani oleh compiler.
Pada pemikiran pertama, solusi ini cukup hacky .
Apakah ini cara penambahan baru ke bahasa C ++ sekarang akan diimplementasikan: dengan peran implisit dari beberapa kelas template dan bukan oleh bahasa inti ?
Harap pertimbangkan contoh-contoh ini:
widget<int> w = {1,2,3}; //this is how we want to use a class
mengapa kelas baru dipilih:
widget( std::initializer_list<T> init )
alih-alih menggunakan sesuatu yang mirip dengan salah satu ide ini:
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
- array klasik, Anda mungkin bisa menambahkan
const
di sana-sini - tiga titik sudah ada dalam bahasa (var-args, sekarang template variadic), mengapa tidak menggunakan kembali sintaks (dan membuatnya terasa built-in )
- hanya wadah yang ada, bisa menambahkan
const
dan&
Semuanya sudah menjadi bagian dari bahasa. Saya hanya menulis 3 ide pertama saya, saya yakin ada banyak pendekatan lain.
sumber
std::array<T>
tidak lebih dari 'bagian dari bahasa'std::initializer_list<T>
. Dan ini hampir bukan satu-satunya komponen pustaka yang diandalkan oleh bahasa tersebut. Lihatnew
/delete
,,type_info
berbagai jenis pengecualiansize_t
, dll.const T(*)[N]
, karena itu berperilaku sangat mirip dengan carastd::initializer_list
kerjanya.std::array
atau array berukuran statis adalah alternatif yang kurang diinginkan.Jawaban:
Sudah ada contoh fitur bahasa "inti" yang mengembalikan tipe yang ditentukan dalam
std
namespace.typeid
kembalistd::type_info
dan (peregangan satu poin mungkin)sizeof
kembalistd::size_t
.Dalam kasus sebelumnya, Anda sudah perlu menyertakan tajuk standar untuk menggunakan apa yang disebut fitur "bahasa inti".
Sekarang, untuk daftar penginisialisasi kebetulan tidak ada kata kunci yang diperlukan untuk menghasilkan objek, sintaksnya adalah kurung kurawal sensitif konteks. Selain itu, itu sama dengan
type_info
. Secara pribadi saya tidak berpikir tidak adanya kata kunci membuatnya "lebih hacky". Mungkin sedikit lebih mengejutkan, tetapi ingat bahwa tujuannya adalah untuk memungkinkan sintaks braced-initializer yang sama yang telah diizinkan untuk agregat.Jadi ya, Anda mungkin dapat mengharapkan lebih banyak dari prinsip desain ini di masa mendatang:
std
bukan sebagai bawaan.Karenanya:
std
.Apa yang terjadi, menurut saya, adalah bahwa tidak ada pembagian mutlak dalam C ++ antara "bahasa inti" dan pustaka standar. Mereka adalah bab yang berbeda dalam standar tetapi masing-masing mereferensikan yang lain, dan selalu demikian.
Ada pendekatan lain dalam C ++ 11, yaitu lambda memperkenalkan objek yang memiliki tipe anonim yang dihasilkan oleh kompilator. Karena mereka tidak memiliki nama, mereka tidak ada di namespace sama sekali, tentu tidak ada di dalamnya
std
. Itu bukan pendekatan yang cocok untuk daftar penginisialisasi, karena Anda menggunakan nama tipe saat Anda menulis konstruktor yang menerimanya.sumber
type_info
dansize_t
merupakan argumen yang bagus .. yahsize_t
hanya sebuah typedef .. jadi mari kita lewati ini. Perbedaan antaratype_info
daninitializer_list
adalah bahwa yang pertama adalah hasil dari operator eksplisit , dan yang kedua dari tindakan kompilator implisit . Menurut saya, itu jugainitializer_list
bisa diganti dengan beberapa kontainer yang sudah ada .. atau lebih baik lagi: setiap pengguna menyatakan sebagai tipe argumen!vector
itu,array
maka Anda dapat membuat vektor dari larik apa pun dengan tipe yang tepat, bukan hanya yang dihasilkan oleh sintaks daftar penginisialisasi. Saya tidak yakin itu akan menjadi hal yang buruk untuk membangun kontainer dari salah satuarray
, tapi itu bukan maksud panitia dalam memperkenalkan sintaks baru.std::array
bahkan tidak memiliki konstruktor. Thestd::array
kasus hanya agregat-inisialisasi. Juga, saya menyambut Anda untuk bergabung dengan saya di ruang obrolan Lounge <C ++>, karena diskusi ini agak lama.Komite Standar C ++ tampaknya memilih untuk tidak menambahkan kata kunci baru, mungkin karena itu meningkatkan risiko pemecahan kode yang ada (kode lama dapat menggunakan kata kunci tersebut sebagai nama variabel, kelas, atau apa pun).
Selain itu, menurut saya, mendefinisikan
std::initializer_list
sebagai penampung templated adalah pilihan yang cukup elegan: jika itu adalah kata kunci, bagaimana Anda mengakses jenis yang mendasarinya? Bagaimana Anda akan mengulanginya? Anda juga memerlukan banyak operator baru, dan itu hanya akan memaksa Anda mengingat lebih banyak nama dan lebih banyak kata kunci untuk melakukan hal yang sama seperti yang dapat Anda lakukan dengan penampung standar.Memperlakukan
std::initializer_list
sebagai wadah lain memberi Anda kesempatan untuk menulis kode umum yang bekerja dengan hal-hal itu.MEMPERBARUI:
Untuk memulainya, semua container lainnya memiliki metode untuk menambahkan, menghapus, dan meletakkan elemen, yang tidak diinginkan untuk koleksi yang dibuat oleh kompilator. Satu-satunya pengecualian adalah
std::array<>
, yang membungkus larik gaya-C ukuran tetap dan karenanya akan tetap menjadi satu-satunya kandidat yang masuk akal.Namun, seperti yang ditunjukkan Nicol Bolas dengan benar dalam komentar, perbedaan mendasar lainnya antara
std::initializer_list
dan semua wadah standar lainnya (termasukstd::array<>
) adalah bahwa wadah terakhir memiliki semantik nilai , sementarastd::initializer_list
memiliki semantik referensi . Menyalinstd::initializer_list
, misalnya, tidak akan menyebabkan salinan elemen yang dikandungnya.Selain itu (sekali lagi, atas kebaikan Nicol Bolas), memiliki wadah khusus untuk daftar inisialisasi brace memungkinkan overloading pada cara pengguna melakukan inisialisasi.
sumber
std::array
. Tetapistd::array
mengalokasikan memori sambilstd::initializaer_list
membungkus array waktu kompilasi. Anggap saja sebagai perbedaan antarachar s[] = "array";
danchar *s = "initializer_list";
.std::array
tidak mengalokasikan memori apa pun, itu sederhanaT arr[N];
, hal yang sama yang mendukungstd::initializer_list
.T arr[N]
mengalokasikan memori, mungkin tidak di heap dinamis tetapi di tempat lain ... Begitu jugastd::array
. Namun yang tidak kosonginitializer_list
tidak dapat dibangun oleh pengguna sehingga jelas tidak dapat mengalokasikan memori.Ini bukanlah hal baru. Misalnya,
for (i : some_container)
mengandalkan keberadaan metode tertentu atau fungsi mandiri disome_container
kelas. C # bahkan lebih mengandalkan pustaka .NET-nya. Sebenarnya, menurut saya, ini adalah solusi yang cukup elegan, karena Anda dapat membuat kelas Anda kompatibel dengan beberapa struktur bahasa tanpa mempersulit spesifikasi bahasa.sumber
begin
danend
metode. Ini IMO sedikit berbeda.iterable class MyClass { };
initializer_list
meskipunIni memang bukan hal baru dan berapa banyak yang telah menunjukkan, praktik ini ada di C ++ dan ada, katakanlah, di C #.
Andrei Alexandrescu telah menyebutkan hal yang baik tentang ini: Anda mungkin menganggapnya sebagai bagian dari namespace "inti" imajiner, maka itu akan lebih masuk akal.
Jadi, itu sebenarnya sesuatu seperti:
core::initializer_list
,core::size_t
,core::begin()
,core::end()
dan sebagainya. Ini hanya kebetulan yang tidak menguntungkan bahwastd
namespace memiliki beberapa konstruksi bahasa inti di dalamnya.sumber
Tidak hanya dapat bekerja sepenuhnya di perpustakaan standar. Dimasukkan ke dalam pustaka standar tidak berarti bahwa kompiler tidak dapat memainkan trik-trik cerdas.
Meskipun mungkin tidak dapat dalam semua kasus, itu mungkin mengatakan dengan sangat baik: jenis ini terkenal, atau tipe sederhana, mari kita abaikan
initializer_list
dan hanya memiliki gambaran memori tentang apa nilai yang diinisialisasi seharusnya.Dengan kata lain
int i {5};
dapat setara denganint i(5);
atauint i=5;
atau bahkanintwrapper iw {5};
Di manaintwrapper
kelas pembungkus sederhana di atas int dengan konstruktor sepele mengambilinitializer_list
sumber
int i {5}
melibatkan apa punstd::initializer_list
salah.int
tidak ada konstruktor yang mengambilstd::initializer_list
, jadi5
hanya digunakan secara langsung untuk membuatnya. Jadi contoh utamanya tidak relevan; tidak ada optimasi yang harus dilakukan. Di luar itu, karenastd::initializer_list
melibatkan kompiler yang membuat dan membuat proxy array 'imajiner', saya kira ini dapat mendukung pengoptimalan, tetapi itu adalah bagian 'ajaib' dalam kompiler, jadi ini terpisah dari apakah pengoptimal secara umum dapat melakukan sesuatu yang pintar dengan yang cantik. objek tumpul yang berisi 2 iterator yang menghasilkanIni bukan bagian dari bahasa inti karena dapat diimplementasikan sepenuhnya di perpustakaan, hanya baris
operator new
danoperator delete
. Keuntungan apa yang akan membuat kompiler lebih rumit untuk membuatnya?sumber