Let's say I have a class with a member array of std::atomic
s, where the
array is sized via a computation (i.e. it may change based on other constants elsewhere in the program):
class Foo {
static constexpr size_t kArraySize = ComputeArraySize();
std::atomic<size_t> atomics_[kArraySize];
};
What is the most elegant way to ensure that the atomics are all initialized to
zero? Can I do better than looping over the array in Foo
's constructor and
explicitly storing zero? Does the answer differ for std::array
?
Normally I would use a brace initializer here, but the derived length (which may be long) makes it difficult.
Note that I cannot assume that the instance of Foo
has static storage
duration.
Okay, I believe I've worked this through. Both of these will initialize all of the atomics to zero:
std::atomic<size_t> plain_array[kArraySize] = {};
std::array<std::atomic<size_t>, kArraySize> std_array = {};
Here's the logic:
[dcl.init.aggr]/1 defines arrays to be aggregates.
[array.cons]/1 mandates that std::array
also be an aggregate.
[dcl.init.aggr]/7 says that if there are fewer elements of the initializer list than there are members in the aggregate, then the remaining members shall be initialized from an empty initializer list. In this case, that's all members.
[dcl.init.list]/3 defines list-initialization from an empty list for a class
with a default constructor (as with std::atomic
) to cause
value-initialization.
[dcl.init]/7 says that classes without user-provided constructors are
zero-initialized. Assuming that std::array<T>
contains an array of T
,
and that the zero representation of std::atomic<size_t>
is what we expect,
then we're good.
Now, std::atomic
does have a user-provided constructor, just not a
user-provided default constructor (the latter is explicitly defaulted). So
it doesn't technically fit the conditions of the last point. But it seems this
is an error in the standard, and has been fixed in more recent
drafts.