Consider:
void foo() {
std::vector<std::atomic<int>> foo(10);
...
}
Are the contents of foo now valid? Or do I need to explicitly loop through and initialise them? I have checked on Godbolt and it seems fine, however the standard seems to be very confused on this point.
The std::vector constructor says it inserts default-inserted instances of std::atomic<int>
, which are value initialised via placement new
.
I think this effect of value initialisation applies:
2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
So it seems to me that the atomics are zero-initialised. So the question is, does zero-initialisation of a std::atomic<int>
result in a valid object?
I'm going to guess that the answer is "yes in practice but it's not really defined"?
Note: This answer agrees that it is zero-initialised, but doesn't really say if that means that the object is valid.
You are correct to be worried. According to standard the atomics has the default constructor called, however they have not been initialized as such. This is because the default constructor doesn't initialize the atomic:
The default-initialized
std::atomic<T>
does not contain aT
object, and its only valid uses are destruction and initialization by std::atomic_init
This is somewhat in violation of the normal language rules, and some implementations initialize anyway (as you have noted).
That being said, I would recommend taking the extra step to make 100% sure they are initialized correctly according to standard - after all you are dealing with concurrency where bugs can be extremely hard to track down.
There are many ways to dodge the issue, including using wrapper:
struct int_atomic {
std::atomic<int> atomic_{0};//use 'initializing' constructor
};
Even if the default constructor were called (it isn't, because it's trivial) it doesn't really do anything.
Zero-initialisation obviously cannot be guaranteed to produce a valid atomic; this'll only work if by chance a valid atomic is created by zero-initialising all its members.
And, since atomics aren't copyable, you can't provide a initialisation value in the vector constructor.
You should now loop over the container and std::atomic_init
each element. If you need to lock around this, that's fine because you're already synchronising the vector's creation for the same reason.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With