Initialisation du vecteur atomique

12

Considérer:

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

Le contenu de foo est-il maintenant valide? Ou dois-je les parcourir explicitement et les initialiser? J'ai vérifié Godbolt et ça semble bien, mais la norme semble être très confuse sur ce point.

Le constructeur std :: vector dit qu'il insère des instances insérées par défaut de std::atomic<int>, dont la valeur est initialisée via placement new.

Je pense que cet effet d'initialisation de la valeur s'applique:

2) si T est un type de classe avec un constructeur par défaut qui n'est ni fourni par l'utilisateur ni supprimé (c'est-à-dire qu'il peut s'agir d'une classe avec un constructeur par défaut implicitement défini ou par défaut), l'objet est initialisé à zéro puis il est initialisé par défaut s'il a un constructeur par défaut non trivial;

Il me semble donc que les atomes sont initialisés à zéro. La question est donc de savoir si l'initialisation à zéro d'un std::atomic<int>résultat aboutit à un objet valide?

Je vais deviner que la réponse est "oui dans la pratique mais ce n'est pas vraiment défini"?

Remarque: Cette réponse convient qu'il est initialisé à zéro, mais ne dit pas vraiment si cela signifie que l'objet est valide.

Timmmm
la source

Réponses:

7

Vous avez raison de vous inquiéter. Selon la norme, l'atomique a le constructeur par défaut appelé, mais ils n'ont pas été initialisés en tant que tels. C'est parce que le constructeur par défaut n'initialise pas l'atomique:

L'initialisation par défaut std::atomic<T>ne contient pas d' Tobjet, et ses seules utilisations valides sont la destruction et l'initialisation par std :: atomic_init

C'est quelque peu en violation des règles de langage normales, et certaines implémentations s'initialisent quand même (comme vous l'avez noté).

Cela étant dit, je recommanderais de prendre une mesure supplémentaire pour vous assurer à 100% qu'ils sont correctement initialisés conformément à la norme - après tout, vous avez affaire à une concurrence où les bogues peuvent être extrêmement difficiles à localiser.

Il existe de nombreuses façons d'esquiver le problème, notamment en utilisant un wrapper:

struct int_atomic {
   std::atomic<int> atomic_{0};//use 'initializing' constructor
};
Darune
la source
Ou utiliser réellement atomic_init. Vous devez déjà synchroniser autour du code de la question de toute façon
Lightness Races in Orbit
Le constructeur par défaut est trivial, il n'est donc pas appelé de toute façon (selon la citation dans la question)
Lightness Races in Orbit
@LightnessRaceswithMonica qui est également possible, je voulais juste mettre en évidence le wrapper
darune
@LightnessRaceswithMonica c'est une exception aux règles de langage normales - même si certains compilateurs n'implémentent pas cette exception. Je ne suis pas sûr que la réponse StoreTeller soit exacte à 100%.
darune
2

Même si le constructeur par défaut était appelé (ce n'est pas le cas, car c'est trivial), il ne fait vraiment rien .

Une initialisation nulle ne peut évidemment pas être garantie pour produire un atomique valide; cela ne fonctionnera que si par hasard un atomique valide est créé en initialisant zéro tous ses membres.

Et, comme l'atomique n'est pas copiable, vous ne pouvez pas fournir de valeur d'initialisation dans le constructeur vectoriel.

Vous devez maintenant faire une boucle sur le conteneur et std::atomic_initchaque élément. Si vous avez besoin de contourner cela, c'est bien parce que vous synchronisez déjà la création du vecteur pour la même raison.

Courses de légèreté en orbite
la source
@darune Je considère que c'est une sorte de synchronisation;)
Courses de légèreté en orbite