Di C ++ 11, kita memiliki sintaks baru untuk menginisialisasi kelas yang memberi kita banyak kemungkinan bagaimana menginisialisasi variabel.
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
}
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}
Untuk setiap variabel yang saya nyatakan, saya harus memikirkan sintaks inisialisasi mana yang harus saya gunakan dan ini memperlambat kecepatan pengkodean saya. Saya yakin itu bukan maksud memperkenalkan tanda kurung kurawal.
Dalam hal kode template, mengubah sintaks dapat menghasilkan arti yang berbeda, jadi pergi ke cara yang benar sangatlah penting.
Saya bertanya-tanya apakah ada pedoman universal sintaks mana yang harus dipilih.
c++
c++11
initializer-list
helami
sumber
sumber
Jawaban:
Saya pikir yang berikut ini bisa menjadi pedoman yang baik:
Jika nilai (tunggal) yang Anda inisialisasi dimaksudkan untuk menjadi nilai yang tepat dari objek, gunakan
=
inisialisasi copy ( ) (karena jika terjadi kesalahan, Anda tidak akan pernah secara tidak sengaja memanggil konstruktor eksplisit, yang umumnya menafsirkan nilai yang diberikan berbeda). Di tempat di mana inisialisasi salinan tidak tersedia, lihat apakah inisialisasi brace memiliki semantik yang benar, dan jika demikian, gunakan itu; jika tidak, gunakan inisialisasi tanda kurung (jika itu juga tidak tersedia, Anda beruntung).Jika nilai yang Anda inisialisasi adalah daftar nilai yang akan disimpan dalam objek (seperti elemen vektor / larik, atau bagian nyata / imajiner dari bilangan kompleks), gunakan inisialisasi kurung kurawal jika tersedia.
Jika nilai yang Anda inisialisasi bukanlah nilai yang akan disimpan, tetapi mendeskripsikan nilai / status objek yang diinginkan, gunakan tanda kurung. Contohnya adalah argumen ukuran dari sebuah
vector
atau argumen nama file dari sebuahfstream
.sumber
T {}
atau alasan sintaksis seperti penguraian yang paling menjengkelkan ), tetapi secara umum menurut saya ini merupakan saran yang baik. Perhatikan bahwa ini adalah pendapat subjektif saya, jadi orang harus melihat jawaban lain juga.type var{};
tidak.Saya cukup yakin tidak akan pernah ada pedoman universal. Pendekatan saya adalah menggunakan kurung kurawal untuk mengingat hal itu
Jadi kawat gigi bulat dan keriting tidak bisa saling menggantikan. Tetapi mengetahui di mana perbedaannya memungkinkan saya untuk menggunakan inisialisasi kurung kurawal dalam banyak kasus (beberapa kasus di mana saya tidak bisa saat ini adalah bug kompiler).
sumber
int i = 0;
Saya rasa tidak ada orang yang akan menggunakannya diint i{0}
sana, dan mungkin membingungkan (juga,0
jika tipeint
, jadi tidak akan ada penyempitan ). Untuk yang lainnya, saya akan mengikuti saran Juancho: prefer {}, waspadalah terhadap beberapa kasus yang tidak semestinya Anda lakukan. Perhatikan bahwa tidak banyak tipe yang akan mengambil daftar penginisialisasi sebagai argumen konstruktor, Anda dapat mengharapkan kontainer dan tipe seperti kontainer (tuple ...) memilikinya, tetapi kebanyakan kode akan memanggil konstruktor yang sesuai.int i{some floating point}
adalah kesalahan, daripada memotongnya secara diam-diam.{}
maksud "menginisialisasi" kecuali Anda benar - benar tidak bisa .Di luar kode generik (yaitu template), Anda dapat (dan saya melakukannya) menggunakan tanda kurung di mana saja . Salah satu keuntungannya adalah ia berfungsi di mana-mana, misalnya bahkan untuk inisialisasi di dalam kelas:
atau untuk argumen fungsi:
Untuk variabel saya tidak terlalu memperhatikan antara gaya
T t = { init };
atauT t { init };
, saya menemukan perbedaannya kecil dan paling buruk hanya akan menghasilkan pesan kompilator yang membantu tentang penyalahgunaanexplicit
konstruktor.Untuk tipe yang menerima
std::initializer_list
meskipun jelas terkadang non-std::initializer_list
konstruktor dibutuhkan (contoh klasiknyastd::vector<int> twenty_answers(20, 42);
). Tidak apa-apa jika tidak menggunakan kawat gigi.Ketika sampai pada kode generik (yaitu dalam template), paragraf terakhir seharusnya telah memunculkan beberapa peringatan. Pertimbangkan hal berikut:
Kemudian
auto p = make_unique<std::vector<T>>(20, T {});
buat vektor ukuran 2 ifT
is egint
, atau vector size 20 ifT
isstd::string
. Tanda yang sangat jelas bahwa ada sesuatu yang sangat salah terjadi di sini adalah bahwa tidak ada sifat yang dapat menyelamatkan Anda di sini (misalnya dengan SFINAE):std::is_constructible
dalam hal inisialisasi langsung, sedangkan kami menggunakan inisialisasi brace yang menolak mengarahkan- inisialisasi jika dan hanya jika tidak ada konstruktor yang melakukanstd::initializer_list
interferensi. Demikian pulastd::is_convertible
tidak membantu.Saya telah menyelidiki apakah sebenarnya mungkin untuk mengubah sifat yang dapat memperbaikinya, tetapi saya tidak terlalu optimis tentang itu. Bagaimanapun, saya tidak berpikir kita akan kehilangan banyak, saya pikir fakta yang
make_unique<T>(foo, bar)
menghasilkan konstruksi yang setara denganT(foo, bar)
sangat intuitif; terutama mengingat yangmake_unique<T>({ foo, bar })
sangat berbeda dan hanya masuk akal jikafoo
danbar
memiliki tipe yang sama.Karenanya untuk kode generik saya hanya menggunakan tanda kurung untuk inisialisasi nilai (misalnya
T t {};
atauT t = {};
), yang sangat nyaman dan menurut saya lebih unggul dari cara C ++ 03T t = T();
. Jika tidak, itu baik sintaks inisialisasi langsung (yaituT t(a0, a1, a2);
), atau kadang-kadang konstruksi default (T t; stream >> t;
menjadi satu-satunya kasus di mana saya menggunakan yang menurut saya).Itu tidak berarti bahwa semua kawat gigi buruk, pertimbangkan contoh sebelumnya dengan perbaikan:
Ini masih menggunakan tanda kurung kurawal untuk membuat
std::unique_ptr<T>
, meskipun jenis sebenarnya bergantung pada parameter templateT
.sumber
make_unique<T>(20u, T {})
untukT
menjadi salah satuunsigned
ataustd::string
. Tidak terlalu yakin dengan detailnya. (Perhatikan bahwa saya juga mengomentari ekspektasi mengenai inisialisasi langsung vs inisialisasi brace mengenai misalnya fungsi penerusan sempurna.)std::string c("qux");
Belum ditentukan untuk bekerja sebagai inisialisasi di dalam kelas untuk menghindari ambiguitas dengan deklarasi fungsi anggota dalam tata bahasa.