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:
struct foo {
// Ok
std::string a = { "foo" };
// Also ok
std::string b { "bar" };
// Not possible
std::string c("qux");
// For completeness this is possible
std::string d = "baz";
};
atau untuk argumen fungsi:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));
Untuk variabel saya tidak terlalu memperhatikan antara gaya T t = { init };
atau T t { init };
, saya menemukan perbedaannya kecil dan paling buruk hanya akan menghasilkan pesan kompilator yang membantu tentang penyalahgunaan explicit
konstruktor.
Untuk tipe yang menerima std::initializer_list
meskipun jelas terkadang non- std::initializer_list
konstruktor dibutuhkan (contoh klasiknya std::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:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Kemudian auto p = make_unique<std::vector<T>>(20, T {});
buat vektor ukuran 2 if T
is eg int
, atau vector size 20 if T
is std::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 melakukan std::initializer_list
interferensi. Demikian pula std::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 dengan T(foo, bar)
sangat intuitif; terutama mengingat yang make_unique<T>({ foo, bar })
sangat berbeda dan hanya masuk akal jika foo
dan bar
memiliki tipe yang sama.
Karenanya untuk kode generik saya hanya menggunakan tanda kurung untuk inisialisasi nilai (misalnya T t {};
atau T t = {};
), yang sangat nyaman dan menurut saya lebih unggul dari cara C ++ 03 T t = T();
. Jika tidak, itu baik sintaks inisialisasi langsung (yaitu T 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:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
Ini masih menggunakan tanda kurung kurawal untuk membuat std::unique_ptr<T>
, meskipun jenis sebenarnya bergantung pada parameter template T
.