Inicialização do vetor de atômica

12

Considerar:

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

O conteúdo de foo agora é válido? Ou preciso fazer um loop explicito e inicializá-los? Eu verifiquei Godbolt e parece bom, no entanto, o padrão parece estar muito confuso neste ponto.

O construtor std :: vector diz que insere instâncias inseridas por padrão de std::atomic<int>, cujo valor é inicializado via veiculação new.

Eu acho que esse efeito de inicialização de valor se aplica:

2) se T é um tipo de classe com um construtor padrão que não é fornecido nem excluído pelo usuário (ou seja, pode ser uma classe com um construtor padrão implicitamente definido ou com padrão), o objeto é inicializado com zero e, em seguida, é inicializado por padrão se tiver um construtor padrão não trivial;

Portanto, parece-me que os átomos são zero-inicializados. Portanto, a questão é: a inicialização zero de um std::atomic<int>resultado em um objeto válido?

Vou adivinhar que a resposta é "sim na prática, mas não está realmente definida"?

Nota: Esta resposta concorda que foi inicializada com zero, mas não diz se isso significa que o objeto é válido.

Timmmm
fonte

Respostas:

7

Você está certo em estar preocupado. De acordo com o padrão, o atomics tem o construtor padrão chamado, porém eles não foram inicializados como tal. Isso ocorre porque o construtor padrão não inicializa o atômico:

O inicializado por padrão std::atomic<T>não contém um Tobjeto e seus únicos usos válidos são destruição e inicialização por std :: atomic_init

Isso viola as regras normais de linguagem e algumas implementações são inicializadas de qualquer maneira (como você observou).

Dito isto, eu recomendaria dar o passo extra para garantir 100% de certeza de que foram inicializados corretamente de acordo com o padrão - afinal você está lidando com simultaneidade, onde os erros podem ser extremamente difíceis de rastrear.

Há muitas maneiras de evitar o problema, incluindo o uso de wrapper:

struct int_atomic {
   std::atomic<int> atomic_{0};//use 'initializing' constructor
};
darune
fonte
Ou realmente use atomic_init. Você já tem para sincronizar em torno do código em questão de qualquer maneira
Leveza raças em órbita
O construtor padrão é trivial para que ele não é chamado de qualquer maneira (por citação na pergunta)
Leveza raças em órbita
@LightnessRaceswithMonica que também é um possível, eu só want'ed para destacar o wrapper
darune
@LightnessRaceswithMonica, essa é uma exceção às regras normais de linguagem - mesmo que alguns compiladores não implementem essa exceção. Não sei se a resposta do StoreTeller é 100% precisa.
darune
2

Mesmo que o construtor padrão tenha sido chamado (não é, porque é trivial), ele realmente não faz nada .

Obviamente, a inicialização zero não pode garantir um atômico válido; isso só funcionará se por acaso um atômico válido for criado, inicializando zero todos os seus membros.

E, como os átomos não são copiáveis, você não pode fornecer um valor de inicialização no construtor de vetores.

Agora você deve percorrer o contêiner e std::atomic_initcada elemento. Se você precisar bloquear isso, tudo bem, porque você já está sincronizando a criação do vetor pelo mesmo motivo.

Raças de leveza em órbita
fonte
@darune considero que uma espécie de sincronização;)
Leveza raças em órbita