2016-03-31 15 views
5

Che cosa significa 0-inizializzazione della variabile std::atomic<integral_type>?0-initialization of atomics è garantito per impostare il valore member su 0?

Origini della domanda. Ho una funzione statica std::array di std::atomic<std::int>, che voglio essere impostata a 0 prima del primo utilizzo (è ovvio che la funzione in cui risiede l'array è chiamata in modo imprevedibile da più thread).

Questo pezzo di codice è di bell'aspetto, ma non la compilazione a causa di Atomics essere non-copia costruibile:

#include <array> 
#include <atomic> 

void foo() { 
    using t = std::atomic<int>; 
    static std::array<t, 2> arr = {0, 0}; // <-- explicit, but errors out (see below) 
    static std::array<t, 2> arr2; // <-- implicit?, works 
} 

error: use of deleted function ‘std::atomic::atomic(const std::atomic&)’ std::array arr = {0, 0};

Ora, capisco che statica std::array sta per 0-inizializzare tutte è membri e std::atomic<> sta per essere inizializzato 0. Ma abbiamo una garanzia esplicita o implicita che imposterà tutti i valori a? Il buon senso dice "sì" - dopotutto, supponiamo che la classe abbia un membro di tipo int e questo membro sia inizializzato a 0. Ma questa supposizione è basata su solidi motivi di standard?

+0

Interessante. Questo produce l'errore in [GCC] (http://coliru.stacked-crooked.com/a/467922a19099c9a7) e [Clang] (http://coliru.stacked-crooked.com/a/3c8e5cba2847b829), ma non con MSVC++. Un effetto collaterale forse di [copia elision] (http://en.cppreference.com/w/cpp/language/copy_elision)? – wally

+1

@flatmouse MSVC è bacato, come tutti sappiamo :) – SergeyA

+0

Penso che tu voglia dire pieno di 'caratteristiche' extra e non documentate :) – wally

risposta

4

Usa (normalmente ridondante) bretelle per evitare copia-inizializzazione:

static t arr[2] = {{0}, {0}}; 
static std::array<t, 2> arr2 = {{{0}, {0}}}; /* Need extra pair here; otherwise {0} is 
               treated as the initializer of the internal 
               array */ 

Demo. Quando omettiamo le parentesi, stiamo inizializzando la copia, il che richiede la creazione e la copia temporanea di una copia. Con le parentesi, abbiamo l'inizializzazione della lista di copia, che funziona come l'inizializzazione della lista diretta (ad esempio, inizializza ogni elemento con {0}, che va bene).

È anche possibile attendere che venga introdotto guaranteed copy elision e utilizzare la sintassi.

+0

Sì, potrei fare call_once, ma non c'è bellezza in esso. Mi piacerebbe che la mia sintassi fosse garantita :) – SergeyA

+0

@SergeyA La tua sintassi è, così com'è, mal formata. Passerà un po 'di tempo prima che la proposta a cui mi collego venga incorporata. – Columbo

+0

Bene, su cosa si basa questa affermazione? Quel atomizzatore di inizializzazione 0 non sta per inizializzare il mio membro 0? Continuo a fare l'upvoting per il pezzo di elision copiato, e se non uscirà niente di meglio, accetterò lo stesso. – SergeyA

0

Da cppreference, la documentazione del costruttore di default di std :: atomica dice:

Constructs new atomic variable.

1) The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects. std::atomic_init may be used to complete initialization.

quindi sarà necessario ciclo di inizializzazione di Colombo di sicuro.

+0

Bene, c'è un'inizializzazione 0 che si verifica a causa del fatto che la matrice è statica. La domanda è, cosa significa 0-initializaion per i valori all'interno dell'atomica. – SergeyA

+0

@SergeyA Inizializzazione 0 o inizializzazione predefinita? –

+0

0 inizializzazione in questo caso. 'static' lo fa per te. – SergeyA

0

Ciò che è necessario differenziare è l'inizializzazione di default e l'inizializzazione zero. Ho parlato di questo argomento molto tempo fa e sono giunto alla conclusione che lo standard implicitamente richiede che la classe atomica agisca in modo identico alle strutture quando si tratta di inizializzare.

Il tipo di base non atomico deve essere banalmente copiabile e il tipo atomico deve supportare l'inizializzazione predefinita e essere inizializzato staticamente con (e senza) ATOMIC_VAR_INIT. Non ci sono soluzioni pulite che potrei trovare che non finiscano per usare una struttura interna o una derivata da una struct (ho scritto un'implementazione atomica per un RT OS interno).

Quindi lo standard, se non lo richiede, guida l'implementazione verso una soluzione in cui l'inizializzazione zero fa esattamente ciò che si desidera.

ho fatto un Live Example che confronta il seguente:

std::array<t, 2> default; 
std::array<t, 2> zero{}; 
std::array<t, 2> explicit{{{0},{0}}}; 

si vedrà che l'inizializzazione zero è identica alla versione esplicita, e con gcc 6.3.0 ancora più efficiente.

Giusto per ribadire, non penso che lo standard richieda esplicitamente l'inizializzazione zero per comportarsi in questo modo, ma - a quanto mi risulta - dato ciò che è definito, deve necessariamente.

Problemi correlati