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 explicitkonstruktor.
Untuk tipe yang menerima std::initializer_listmeskipun jelas terkadang non- std::initializer_listkonstruktor 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 Tis eg int, atau vector size 20 if Tis 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_constructibledalam hal inisialisasi langsung, sedangkan kami menggunakan inisialisasi brace yang menolak mengarahkan- inisialisasi jika dan hanya jika tidak ada konstruktor yang melakukan std::initializer_listinterferensi. Demikian pula std::is_convertibletidak 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 foodan barmemiliki 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.