2015-01-28 15 views
17

Giả sử tôi có một tham số kiểu mẫu T.Vị trí mới trong tiêu chuẩn :: aligned_storage?

Và giả sử tôi có một std::aligned_storage như sau:

typename std::aligned_storage<sizeof(T), alignof(T)>::type storage; 

tôi muốn vị trí mới một T vào storage.

Giá trị/loại con trỏ tuân thủ tiêu chuẩn để chuyển cho toán tử vị trí mới là gì và làm thế nào để lấy được từ storage?

new (& ???) T(a,b,c); 

Ví dụ:

new (&storage) T(a,b,c); 
new (static_cast<void*>(&storage)) T(a,b,c); 
new (reinterpret_cast<T*>(&storage)) T(a,b,c); 
new (static_cast<T*>(static_cast<void*>(&storage)); 

nào ở trên (nếu có) là tuân thủ, và nếu không, cách tốt hơn là gì?

Trả lời

30

Cách hoang tưởng nhất là

::new ((void *)::std::addressof(storage)) T(a, b, c); 

Giải thích:

  • ::std::addressof bảo vệ chống quá tải unary operator& trên storage, mà là về mặt kỹ thuật cho phép theo tiêu chuẩn. (Mặc dù không có triển khai lành mạnh nào sẽ làm điều đó.) Các bảo vệ ::std chống lại bất kỳ không gian tên (hoặc các lớp) không phải là cấp cao nhất được gọi là std có thể nằm trong phạm vi.
  • (void *) (trong trường hợp này là static_cast) đảm bảo rằng bạn gọi vị trí operator new lấy một số void * thay vì thứ khác như decltype(storage) *.
  • ::new bỏ qua bất kỳ vị trí cụ thể theo lớp nào operator new s, đảm bảo cuộc gọi đi tới cuộc gọi toàn cầu.

Cùng nhau, điều này đảm bảo rằng cuộc gọi đi đến vị trí thư viện operator new tham gia một void *, và rằng T được xây dựng tại nơi storage là.

Trong hầu hết các chương trình lành mạnh, tuy nhiên,

new (&storage) T(a,b,c); 

nên là đủ.

+7

OK, +1 cho mức độ hoang tưởng tuyệt đối. Nếu tôi đã từng bắt tay vào viết một bài thực hành Hell ++, tôi sẽ yêu cầu bạn cộng tác :-) – Angew

4

Chức năng phân bổ vị trí được mô tả như sau (C++ 14 n4140 18.6.1.3):

void* operator new(std::size_t size, void* ptr) noexcept; 

Returns:ptr.

Ghi chú: Cố ý không thực hiện hành động nào khác.

20.10.7.6 bảng 57 mô tả aligned_storage<Len, Align> như sau:

Thành viên typedef type phải một loại POD phù hợp để sử dụng lưu trữ như chưa được khởi tạo cho bất kỳ đối tượng có kích thước tối đa là Len và có sự liên kết là ước số của Căn chỉnh.

Điều này ngụ ý rằng trong trường hợp của bạn, &storage là phù hợp liên kết để giữ một đối tượng kiểu T. Do đó, trong các trường hợp bình thường , tất cả 4 cách bạn đã liệt kê vị trí gọi new là hợp lệ và tương đương. Tôi sẽ sử dụng cái đầu tiên (new (&storage)) để ngắn gọn.


T.C. được chỉ ra một cách chính xác trong các nhận xét rằng chương trình của bạn có thể tuyên bố tình trạng quá tải của hàm phân bổ lấy typename std::aligned_storage<sizeof(T), alignof(T)>::type*, sau đó sẽ được chọn bằng độ phân giải quá tải thay vì phiên bản 'vị trí mới' do thư viện cung cấp.

Tôi cho rằng điều này có thể không xảy ra trong ít nhất 99.999% trường hợp, nhưng nếu bạn cũng cần bảo vệ chống lại điều đó, hãy sử dụng một trong các phôi đến void*. Trực tiếp static_cast<void*>(&storage) là đủ.

Ngoài ra, nếu bạn hoang tưởng đến mức này, bạn có thể sử dụng ::new thay vì chỉ new để bỏ qua bất kỳ chức năng phân bổ theo lớp cụ thể nào.

+1

Chỉ phiên bản có ký tự 'void *' được đảm bảo chuyển đến vị trí thư viện mới. –

+0

Ngoài ra, để được thêm hoang tưởng, ':: new'. –

Các vấn đề liên quan