Bên ngoài mã chung (tức là các mẫu), bạn có thể (và tôi cũng vậy) sử dụng dấu ngoặc nhọn ở mọi nơi . Một ưu điểm là nó hoạt động ở mọi nơi, chẳng hạn ngay cả khi khởi tạo trong lớp:
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";
};
hoặc cho các đối số hàm:
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));
Đối với các biến mà tôi không chú ý nhiều giữa các T t = { init };
hoặc các T t { init };
kiểu, tôi thấy sự khác biệt là nhỏ và tệ nhất sẽ chỉ dẫn đến một thông báo trình biên dịch hữu ích về việc sử dụng sai một hàm explicit
tạo.
Đối với các kiểu chấp nhận std::initializer_list
mặc dù rõ ràng là đôi khi cần các hàm không phải là std::initializer_list
cấu tử (ví dụ cổ điển là std::vector<int> twenty_answers(20, 42);
). Khi đó không cần niềng răng cũng được.
Khi nói đến mã chung (tức là trong các mẫu), đoạn cuối cùng lẽ ra phải đưa ra một số cảnh báo. Hãy xem xét những điều sau:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Sau đó, auto p = make_unique<std::vector<T>>(20, T {});
tạo một vectơ có kích thước 2 nếu T
là ví dụ int
, hoặc một vectơ có kích thước 20 nếu T
là std::string
. Một dấu hiệu rất rõ ràng rằng có điều gì đó rất sai đang xảy ra ở đây là không có đặc điểm nào có thể cứu bạn ở đây (ví dụ: với SFINAE): std::is_constructible
là về mặt khởi tạo trực tiếp, trong khi chúng tôi đang sử dụng khởi tạo dấu ngoặc nhọn để định hướng thành trực tiếp- khởi tạo nếu và chỉ khi không có hàm tạo nào std::initializer_list
can thiệp. Tương tự std::is_convertible
không có ích gì.
Tôi đã điều tra xem liệu có thực sự có thể tự tay cuộn một đặc điểm có thể khắc phục điều đó hay không nhưng tôi không quá lạc quan về điều đó. Trong mọi trường hợp, tôi không nghĩ rằng chúng ta sẽ thiếu nhiều, tôi nghĩ rằng thực tế make_unique<T>(foo, bar)
dẫn đến kết quả xây dựng tương đương với T(foo, bar)
rất trực quan; đặc biệt là vì điều đó make_unique<T>({ foo, bar })
khá khác nhau và chỉ có ý nghĩa nếu foo
và bar
có cùng một loại.
Do đó, đối với mã chung, tôi chỉ sử dụng dấu ngoặc nhọn để khởi tạo giá trị (ví dụ T t {};
hoặc T t = {};
), điều này rất thuận tiện và tôi nghĩ là tốt hơn so với cách C ++ 03 T t = T();
. Nếu không, đó là cú pháp khởi tạo trực tiếp (tức là T t(a0, a1, a2);
) hoặc đôi khi là cấu trúc mặc định ( T t; stream >> t;
là trường hợp duy nhất tôi sử dụng mà tôi nghĩ).
Điều đó không có nghĩa là tất cả các niềng răng đều xấu, hãy xem xét ví dụ trước với các bản sửa lỗi:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
Điều này vẫn sử dụng dấu ngoặc nhọn để xây dựng std::unique_ptr<T>
, mặc dù kiểu thực tế phụ thuộc vào tham số mẫu T
.