2016-03-31 14 views
5

không 0-khởi của std::atomic<integral_type> biến có ý nghĩa gì?0-initialization atomics đảm bảo để thiết lập các thành viên giá trị 0?

Nguồn gốc của câu hỏi. Tôi có chức năng tĩnh std::array của std::atomic<std::int>, mà tôi muốn được đặt thành 0 trước lần sử dụng đầu tiên (không cần nói, hàm nơi mảng được gọi được gọi theo cách không thể đoán trước từ nhiều luồng).

Đoạn mã này là đẹp, nhưng không phải biên soạn do Atomics là không sao chép constructable:

#include <array> 
#include <atomic> 

void foo() { 
    using t = std::atomic<int>; 
    static std::array<t, 2> arr = {0, 0}; // <-- explicit, but errors out (see below) 
    static std::array<t, 2> arr2; // <-- implicit?, works 
} 

error: use of deleted function ‘std::atomic::atomic(const std::atomic&)’ std::array arr = {0, 0};

Bây giờ, tôi hiểu rằng tĩnh std::array sẽ 0-khởi tạo tất cả đó là thành viên, và std::atomic<> sẽ được khởi tạo 0. Nhưng chúng ta có một gurantee rõ ràng hoặc tiềm ẩn rằng nó sẽ thực sự thiết lập tất cả các giá trị đến 0? Cảm giác thông thường nói 'có' - sau khi tất cả, chúng tôi cho rằng lớp học sẽ có một thành viên thuộc loại int và thành viên này sẽ được khởi tạo 0 lần. Nhưng liệu giả định đó có dựa trên cơ sở vững chắc của tiêu chuẩn?

+0

Thú vị. Điều này tạo ra lỗi trong [GCC] (http://coliru.stacked-crooked.com/a/467922a19099c9a7) và [Clang] (http://coliru.stacked-crooked.com/a/3c8e5cba2847b829), nhưng không phải với MSVC++. Một tác dụng phụ có lẽ là [copy elision] (http://en.cppreference.com/w/cpp/language/copy_elision)? – wally

+1

@flatmouse MSVC là lỗi, như chúng ta đều biết :) – SergeyA

+0

Tôi nghĩ rằng bạn có nghĩa là đầy đủ và không có giấy tờ 'tính năng' :) – wally

Trả lời

4

Sử dụng (thường là không cần thiết) niềng răng để tránh sao chép khởi:

static t arr[2] = {{0}, {0}}; 
static std::array<t, 2> arr2 = {{{0}, {0}}}; /* Need extra pair here; otherwise {0} is 
               treated as the initializer of the internal 
               array */ 

Demo. Khi bỏ qua các dấu ngoặc, chúng tôi đang sao chép-khởi tạo, đòi hỏi một tạm thời được tạo ra và sao chép từ. Với niềng răng, chúng tôi có bản sao-list-khởi tạo, có tác dụng giống như trực tiếp-list-khởi (ví dụ: khởi mỗi phần tử với {0}, đó là tốt).

Bạn cũng có thể đợi đến khi guaranteed copy elision được giới thiệu và chỉ sử dụng cú pháp của bạn.

+0

Vâng, tôi có thể làm call_once, nhưng không có vẻ đẹp trong đó. Tôi rất thích cú pháp của mình để được đảm bảo :) – SergeyA

+0

@SergeyA Cú pháp của bạn là, vì nó là viết tắt, hình thành không đúng. Một thời gian sẽ trôi qua trước khi đề xuất mà tôi liên kết được kết hợp. – Columbo

+0

Vâng, tuyên bố này dựa trên điều gì? Cái nguyên tử khởi tạo 0 đó sẽ không 0-khởi tạo thành viên của tôi? Tôi vẫn upvoting cho guranteed sao chép elision mảnh, và nếu không có gì tốt hơn đi ra, tôi sẽ chấp nhận cho cùng. – SergeyA

0

Từ cppreference, tài liệu của các nhà xây dựng mặc định của std :: nguyên tử nói:

Constructs new atomic variable.

1) The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects. std::atomic_init may be used to complete initialization.

Vì vậy, bạn sẽ cần vòng lặp khởi động Columbo cho chắc chắn.

+0

Vâng, có một 0-khởi tạo diễn ra do mảng được tĩnh. Câu hỏi đặt ra là, những gì 0-initializaion có nghĩa là cho các giá trị bên trong nguyên tử. – SergeyA

+0

@SergeyA 0-initialisation, hoặc khởi tạo mặc định? –

+0

0 khởi tạo trong trường hợp này. 'static' làm điều đó cho ya. – SergeyA

0

Điều bạn cần phân biệt là khởi tạo mặc định và không khởi tạo. Tôi có màu đỏ về chủ đề này một thời gian trước đây, và đi đến kết luận rằng tiêu chuẩn ngầm đòi hỏi lớp nguyên tử hành động giống hệt với các cấu trúc khi nói đến khởi tạo.

Loại cơ sở không nguyên tử cần phải có khả năng sao chép nhỏ, và loại nguyên tử cần hỗ trợ khởi tạo mặc định và được khởi tạo tĩnh với (và không có) ATOMIC_VAR_INIT. Không có bất kỳ bất kỳ giải pháp sạch tôi có thể đưa ra mà không kết thúc bằng cách sử dụng một cấu trúc bên trong hoặc bắt nguồn từ một struct (tôi đã viết một thực hiện nguyên tử cho một trong nhà RT OS).

Vì vậy, tiêu chuẩn nếu không yêu cầu, ít nhất là đánh giá cao việc thực hiện đối với một giải pháp nơi zero-initialization không chính xác những gì bạn muốn.

tôi đã thực hiện một Live Example mà so sánh như sau:

std::array<t, 2> default; 
std::array<t, 2> zero{}; 
std::array<t, 2> explicit{{{0},{0}}}; 

bạn sẽ thấy rằng không khởi là giống hệt với phiên bản rõ ràng, và với gcc 6.3.0 thậm chí hiệu quả hơn.

Chỉ cần nhắc lại, tôi không nghĩ tiêu chuẩn yêu cầu không khởi tạo rõ ràng theo cách đó, nhưng - theo hiểu biết của tôi - được xác định là cần thiết.

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