Как мне элегантно инициализировать массив std ::atomic?

#c #c 11

#c #c 11

Вопрос:

Допустим, у меня есть класс с массивом элементов std::atomic s, где размер массива определяется с помощью вычисления (т. Е. Он может изменяться на основе других констант в другом месте программы):

 class Foo {
  static constexpr size_t kArraySize = ComputeArraySize();
  std::atomic<size_t> atomics_[kArraySize];
};
  

Какой самый элегантный способ гарантировать, что все атомарные элементы инициализированы до
нуля? Могу ли я сделать лучше, чем перебирать массив в Foo конструкторе и
явно сохранять ноль? Отличается ли ответ для std::array ?

Обычно я бы использовал здесь инициализатор фигурных скобок, но производная длина (которая может быть длинной) затрудняет это.

Обратите внимание, что я не могу предположить, что экземпляр Foo имеет статическую продолжительность хранения.

Комментарии:

1. объявить как std::atomic<size_t> atomics_[kArraySize] = {}; ? Я не уверен, какие правила существуют для атомной инициализации

2. … Ничего не делать. Все статическое инициализируется нулем при запуске программы.

3. @Ryan Haining: Да, это работает для простого массива в стиле C. Похоже, это не работает std::array<std::atomic<size_t>, N> , хотя стандарт, похоже, говорит, что это должно быть. @n.m.: «Обратите внимание, что я не могу предположить, что экземпляр Foo имеет статическую продолжительность хранения».

4. «Похоже, не работает для std::array<std::atomic<size_t>, N>». Работает для меня. Какую версию компилятора вы используете?

5. Я могу поручиться за это. Он работает, как и ожидалось, с gcc 4.8.2 и clang 3.4.1.

Ответ №1:

Хорошо, я полагаю, что я проработал это. Оба из них будут инициализировать все атомы до нуля:

 std::atomic<size_t> plain_array[kArraySize] = {};
std::array<std::atomic<size_t>, kArraySize> std_array = {};
  

Вот логика:

  • [dcl.init.aggr]/1 определяет массивы как агрегированные.

  • [array.cons]/1 требует, чтобы это std::array также было агрегированным.

  • [dcl.init.aggr]/7 говорит, что если в списке инициализаторов меньше элементов, чем элементов в совокупности, то остальные элементы должны быть инициализированы из пустого списка инициализаторов. В данном случае это все члены.

  • [dcl.init.list]/3 определяет инициализацию списка из пустого списка для класса с конструктором по умолчанию (как с std::atomic ), чтобы вызвать инициализацию значения.

  • [dcl.init]/7 говорит, что классы без пользовательских конструкторов инициализируются нулем. Предполагая, что std::array<T> он содержит массив T , и что нулевое представление std::atomic<size_t> — это то, что мы ожидаем, тогда все в порядке.

Теперь std::atomic у него есть пользовательский конструктор, просто не пользовательский конструктор по умолчанию (последний явно задан по умолчанию). Таким образом, он технически не соответствует условиям последнего пункта. Но, похоже, это ошибка в стандарте, и она была исправлена в более поздних проектах.

Комментарии:

1. Я считаю, что базовые атомарные объекты остаются в неинициализированном состоянии из-за неожиданного поведения, описанного в open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0883r0.pdf . Инициализация по умолчанию не делает того, что вы ожидаете здесь.

2. @Joe, похоже, был исправлен wg21.link/p0883