Inicjalizacja wektora atomowego

12

Rozważać:

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

Czy zawartość foo jest teraz aktualna? Czy też muszę je przejrzeć i zainicjować? Sprawdziłem Godbolt i wydaje się, że jest w porządku, jednak standard wydaje się być bardzo zagmatwany w tym punkcie.

Konstruktor std :: vector mówi, że wstawia instancje wstawione domyślniestd::atomic<int> , których wartość jest inicjowana poprzez umieszczenie new.

Myślę, że ten efekt inicjalizacji wartości ma zastosowanie:

2) jeśli T jest typem klasy z domyślnym konstruktorem, który nie jest ani dostarczony przez użytkownika, ani usunięty (to znaczy może to być klasa z domyślnie zdefiniowanym lub domyślnym konstruktorem domyślnym), obiekt jest inicjowany na zero, a następnie jest default-initialized, jeśli ma nietrywialny domyślny konstruktor;

Wydaje mi się więc, że atomika jest inicjowana na zero. Pytanie brzmi zatem: czy inicjalizacja zera std::atomic<int>wyniku jest poprawnym obiektem?

Zgaduję, że odpowiedź brzmi „tak w praktyce, ale nie jest tak naprawdę zdefiniowana”?

Uwaga: Ta odpowiedź zgadza się, że jest inicjowana na zero, ale tak naprawdę nie mówi, czy to oznacza, że ​​obiekt jest poprawny.

Timmmm
źródło

Odpowiedzi:

7

Masz rację, że się martwisz. Zgodnie ze standardem atomics ma domyślnego konstruktora o nazwie, jednak nie zostały one jako takie zainicjowane. Wynika to z faktu, że domyślny konstruktor nie inicjuje atomic:

Domyślnie zainicjowany std::atomic<T>nie zawiera Tobiektu, a jego jedynymi prawidłowymi zastosowaniami są zniszczenie i inicjalizacja przez std :: atomic_init

Jest to nieco sprzeczne z normalnymi regułami językowymi, a niektóre implementacje mimo wszystko inicjują (jak zauważyłeś).

Biorąc to pod uwagę, polecam zrobić dodatkowy krok, aby upewnić się w 100%, że zostały poprawnie zainicjowane zgodnie ze standardem - w końcu masz do czynienia z współbieżnością, w której błędy mogą być bardzo trudne do wyśledzenia.

Istnieje wiele sposobów uniknięcia problemu, w tym użycie otoki:

struct int_atomic {
   std::atomic<int> atomic_{0};//use 'initializing' constructor
};
darune
źródło
Lub faktycznie użyć atomic_init. W każdym razie musisz już zsynchronizować kod w pytaniu
Lightness Races in Orbit
Domyślny konstruktor jest trywialny, więc i tak nie jest wywoływany (zgodnie z cytatem w pytaniu)
Lightness Races in Orbit
@LightnessRaceswithMonica, która jest również możliwa, chciałem tylko podkreślić opakowanie
darune
@LightnessRaceswithMonica jest to wyjątek od normalnych reguł językowych - nawet jeśli niektóre kompilatory nie implementują tego wyjątku. Nie jestem pewien, czy odpowiedź StoreTeller jest w 100% dokładna.
darune
2

Nawet jeśli został wywołany domyślny konstruktor (nie jest, ponieważ jest trywialny) , tak naprawdę nic nie robi .

Nie można oczywiście zagwarantować, że zerowa inicjalizacja wytworzy prawidłowy atom; zadziała to tylko wtedy, gdy przypadkowo powstanie prawidłowy atom, inicjując zero wszystkich jego członków.

A ponieważ atomów nie można kopiować, nie można podać wartości inicjalizacyjnej w konstruktorze wektorowym.

Powinieneś teraz zapętlić pojemnik i std::atomic_initkażdy element. Jeśli musisz to zablokować, to dobrze, ponieważ już synchronizujesz tworzenie wektora z tego samego powodu.

Lekkość Wyścigi na orbicie
źródło
@darune Uważam, że to rodzaj synchronizacji;)
Wyścigi lekkości na orbicie