Entrambi i frammenti di codice hanno lo stesso comportamento, anche in presenza di eccezioni generate durante l'inizializzazione.
Questa conclusione si basa sulla (mia interpretazione) i seguenti citazioni del C++ 11 standard (progetto n3337):
- 1 Sezione 6.7 Dichiarazione dichiarazione clausola 4 stati:
L'inizializzazione zero (8.5) di tutte le variabili di ambito di blocco con durata di archiviazione statica (3.7.1) o durata di memorizzazione del thread (3.7.2) viene eseguita prima di qualsiasi altra inizializzazione. L'inizializzazione costante (3.6.2) di un'entità a ambito di blocco con durata di memorizzazione statica, se applicabile, viene eseguita prima che il suo blocco venga immesso per la prima volta. Un'implementazione è autorizzata a eseguire l'inizializzazione anticipata di altre variabili di ambito di blocco con durata di archiviazione statica o di thread nelle stesse condizioni in cui è consentita un'implementazione statica di una variabile con durata di archiviazione statica o di thread nello scope namespace (3.6.2). Altrimenti, tale variabile viene inizializzata il primo controllo di tempo attraverso la sua dichiarazione; tale variabile è considerata inizializzata al completamento della sua inizializzazione. Se l'inizializzazione termina lanciando un'eccezione, l'inizializzazione non è completa, quindi verrà tentata di nuovo la volta successiva che il controllo entra nella dichiarazione. Se il controllo immette la dichiarazione contemporaneamente durante l'inizializzazione della variabile, l'esecuzione simultanea deve attendere il completamento dell'inizializzazione.88 Se il controllo rientra la dichiarazione in modo ricorsivo mentre la variabile viene inizializzata, il comportamento non è definito.
Ciò significa che in:
void f () {
static bool b = ([ ] { a = A {....}; } (), true);
}
b
è garantito per essere inizializzato una sola volta, il che significa lambda viene eseguito (con successo) una sola volta, il che significa a = A {...};
viene eseguito (con successo) una sola volta.
- 2 Sezione 30.4.4.2 Funzione chiamata una volta stati:
Un'esecuzione di call_once che non chiama la sua func è un'esecuzione passiva. Un'esecuzione di call_once che chiama la sua funzione è un'esecuzione attiva. Un'esecuzione attiva deve chiamare INVOKE (DECAY_COPY (std :: forward (func)), DECAY_COPY (std :: forward (args)) ...). Se tale chiamata a func genera un'eccezione, l'esecuzione è eccezionale, altrimenti restituisce. Un'eccezionale esecuzione deve propagare l'eccezione al chiamante di call_once. Tra tutte le esecuzioni di call_once per ogni dato once_flag: al massimo una sarà un'esecuzione di ritorno; se c'è un'esecuzione di ritorno, deve essere l'ultima esecuzione attiva; e ci sono esecuzioni passive solo se c'è un'esecuzione di ritorno.
Ciò significa che in:
void f () {
call_once (once, [ ] { a = A {....}; });
viene eseguito l'argomento lambda a std::call_once
(con successo) una sola volta, il che significa a = A {...};
viene eseguito (con successo) una sola volta.
In entrambi i casi, a = A{...};
viene eseguito (con successo) una sola volta.
I tuoi due esempi non sono equivalenti, 'call_once' ignora il valore di ritorno dell'oggetto richiamabile che lo hai passato. Probabilmente vuoi assegnare a 'a' in entrambe le espressioni lambda. –
sì, ho dimenticato di cambiare prima. corretto – pal