Инициализация вектора атомности

12

Рассматривать:

void foo() {
  std::vector<std::atomic<int>> foo(10);
  ...
}

Содержимое foo теперь действительно? Или мне нужно явно выполнить цикл и инициализировать их? Я проверил на Godbolt, и, кажется, все в порядке, однако стандарт кажется очень запутанным в этом вопросе.

Конструктор std :: vector говорит, что он вставляет вставленные по умолчанию экземпляры std::atomic<int>, которые являются значением, инициализированным посредством размещения new.

Я думаю, что этот эффект инициализации значения применяется:

2) если T является типом класса с конструктором по умолчанию, который не предоставлен и не удален пользователем (то есть это может быть класс с неявно определенным или дефолтным конструктором по умолчанию), объект инициализируется нулями, а затем инициализируется по умолчанию, если у него есть нетривиальный конструктор по умолчанию;

Так что мне кажется, что атомика инициализируется нулями. Таким образом, вопрос заключается в том, имеет ли нулевая инициализация std::atomic<int>результата в допустимом объекте?

Я собираюсь догадаться, что ответ "да на практике, но это не совсем определено"?

Примечание. Этот ответ согласен с тем, что инициализируется нулем, но в действительности не говорит, означает ли это, что объект допустим.

Timmmm
источник

Ответы:

7

Вы правильно беспокоиться. Согласно стандарту атомика имеет конструктор по умолчанию, но они не были инициализированы как таковые. Это потому, что конструктор по умолчанию не инициализирует атомарный:

Инициализированный по умолчанию std::atomic<T>не содержит Tобъект, и его единственное допустимое использование - уничтожение и инициализация с помощью std :: atomic_init

Это несколько нарушает правила обычного языка, и некоторые реализации все равно инициализируются (как вы заметили).

При этом я бы порекомендовал сделать дополнительный шаг, чтобы убедиться на 100%, что они правильно инициализируются в соответствии со стандартом - после всего, что вы имеете дело с параллелизмом, где ошибки могут быть чрезвычайно трудно отследить.

Есть много способов избежать этой проблемы, включая использование оболочки:

struct int_atomic {
   std::atomic<int> atomic_{0};//use 'initializing' constructor
};
darune
источник
Или на самом деле использовать atomic_init. В любом случае, вы уже должны синхронизироваться вокруг кода, о котором идет речь
Гонки
Конструктор по умолчанию тривиален, так что он все равно не вызывается (согласно цитате в вопросе)
Гонки
@LightnessRaceswithMonica, это тоже возможно, я просто хочу выделить обертку
Дарун
@LightnessRaceswithMonica - это исключение из правил обычного языка - хотя некоторые компиляторы не реализуют это исключение. Я не уверен, что ответ StoreTeller на 100% точен.
darune
2

Даже если был вызван конструктор по умолчанию (это не так, потому что это тривиально), он на самом деле ничего не делает .

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

И, поскольку атомы не могут быть скопированы, вы не можете предоставить значение инициализации в конструкторе вектора.

Теперь вы должны перебрать контейнер и std::atomic_initкаждый элемент. Если вам нужно обойти это, это нормально, потому что вы уже синхронизируете создание вектора по той же причине.

Гонки легкости на орбите
источник
@darune Я считаю, что это своего рода синхронизация;)
Гонки