Does atomic science require 0-initialization to set the value to 0?

What does 0-initialization of std::atomic<integral_type> mean?

The origin of the question. I have a static function std::array of std::atomic<std::int> that I want to set to 0 before first use (needless to say, the function the array is in is called in an unpredictable way from multiple threads) .

This code fragment is beautiful, but not compiled due to the impossibility of creating an atom instance:

 #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: using the remote function 'std :: atomic :: atomic (const std :: atomic &) std :: array arr = {0, 0};

Now I understand that static std::array is going to 0-initialize all its members, and std::atomic<> will be 0-initialized. But does the explicit or implicit guarantor have that he will really set all the values to 0? Common sense says yes - in the end, we assume that the class will have a member of type int , and this member will be 0-initialized. But is this assumption based on the solid foundations of the standard?

+5
source share
3 answers

Use (usually redundant) braces to avoid copy initialization:

 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 When braces are disabled, we perform a copy, which requires temporary creation and copying. Using parentheses, we have the initialization of the copy list, which acts the same as the initialization of the direct list (that is, it initializes each item with {0} , which is good).

You can also wait until guaranteed elision copying appears and just use your syntax.

+4
source

From cppreference, the default constructor documentation std :: atomic says:

Creates a new atomic variable.

1) The default constructor is trivial: initialization is not performed, except for zero initialization of static and thread-local objects. std :: atomic_init can be used to complete initialization.

So, you need a Columbo initialization loop.

0
source

What you need to distinguish is the default initialization and zero initialization. I’ve blushed for a long time on this topic, and came to the conclusion that the standard implicitly requires that the atomic class act identically to structures when it comes to initialization.

The non-atomic base type must be trivially copied, and the atomic type must both support default initialization and statically statically with (and without) ATOMIC_VAR_INIT . No pure solutions that I could come up with are obtained using the internal structure or the derivation from the structure (I wrote an atomic implementation for my own RT-system).

Thus, the standard, if not required, at least directs the implementation strongly towards a solution where zero initialization does exactly what you want.

I made a Live Example that compares the following:

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

you'll see that zero initialization is identical to the explicit version, and gcc 6.3.0 is even more efficient.

Just to repeat, I do not think that the standard explicitly requires that zero initialization behave in this way, but, to my understanding, given what is defined, it must be.

0
source

Source: https://habr.com/ru/post/1246165/


All Articles