2013-01-06 16 views
23

Ho una classe con una variabile membro atomica:C++ 11: costruttore di movimento di scrittura con membro atomico <bool>?

struct Foo 
{ 
    std::atomic<bool> bar; 
    /* ... lots of other stuff, not relevant here ... */ 
    Foo() 
    : bar(false) 
    {} 

    /* Trivial implementation fails in gcc 4.7 with: 
    * error: use of deleted function ‘std::atomic<bool>::atomic(const td::atomic<bool>&)’ 
    */ 
    Foo(Foo&& other) 
    : bar(other.bar) 
    {} 
}; 

Foo f; 
Foo f2(std::move(f)); // use the move 

Come dovrebbe essere spostare aspetto costruttore come?

Gcc 4.7 non piace nessuno dei miei tentativi (come l'aggiunta di std::move() intorno al other.bar) e la rete è sorprendentemente tranquillo qui ...

risposta

14

Dal momento che si sta spostando other, nessun altro l'accedervi. Quindi leggere dal suo bar è sicuro, se è atomico o meno.

atomic<T> ha solo due costruttori, uno è il valore predefinito (), l'altro è (T). Quindi, sembra che il tuo codice dovrebbe essere compilato. In caso contrario, cosa succede se si statica_cast other.bar a T, per l'applicazione del costruttore (T) da utilizzare?

: bar(static_cast<bool>(other.bar))

o che è uguale a, e forse meno brutta:

: bar(other.bar.load())

+1

Grazie, la barra (other.bar.load()) '' è la soluzione giusta che è la compilazione ora! – Chris

+7

_So, il tuo codice sembra come dovrebbe essere compilato. No, 'atomic ' ha un costruttore di copie cancellato e la risoluzione di overload non trova il costruttore 'atomic (T)'. Il cast o il carico è necessario. –

+0

@JonathanAvviamente hai ragione, pensavo che il compilatore avrebbe continuato con il prossimo costruttore possibile '(T)' quando sa che il costruttore di copie è stato cancellato e c'è un 'operatore T'. Il motivo è che il costruttore atomico di '(T)' è 'constexpr' e si comporta in modo simile ai costruttori' espliciti' in quanto le conversioni implicite devono essere esplicite (o in questo caso 'constexpr')? –

22

std::atomic non è copiabile o mobile perché its copy constructor is deleted e nessun costruttore mossa è definito. Devi caricare esplicitamente l'altro valore e usarlo per costruire il nuovo valore, come è stato indicato nella risposta di gustaf.

Perché lo std::atomic non è mobile? Essendo una primitiva di sincronizzazione, tutti i thread devono sincronizzarsi sugli stessi dati (ad es. Lo stesso indirizzo). Quando copi (o sposti) un valore atomico, devi usare un certo protocollo di comunicazione. Può essere semplice, come nel tuo esempio (basta caricarlo e usarlo per inizializzare il nuovo atomico) ma, in generale, penso che sia una buona decisione di progettazione del C++ 11 costringerti a pensarci. In caso contrario, il codice potrebbe risultare soddisfacente, ma presenta alcuni problemi di sincronizzazione.

+1

Sto cercando di capire una situazione in cui potrebbe essere problematico avere un atomico spostato automaticamente quando si hanno più thread. Forzare manualmente una mossa con std :: move() è un'altra cosa. Ad ogni modo, mi piacerebbe un esempio per mostrare dove spostare un atomico sarebbe problematico. È possibile spostare qualsiasi altro contenitore 'std' e ci si aspetta che si occupi della sincronizzazione. –

+1

Mi piacerebbe anche sapere se sarebbe una cattiva idea se 'atomic' avesse un costruttore di movimento che dovrebbe essere attivato chiamando esplicitamente' std :: move() '- mi avrebbe dato un'interfaccia che Mi sarei aspettato. Ma sono troppo lontano da essere un esperto di C++ per giudicare se quella decisione sullo standard ha uno scopo o è un errore ... – Chris

+1

Non c'è modo per il costruttore di mosse di decidere se il valore di rvalue è risultato da un esplicito 'std :: move' o è un valore implicito (ad esempio, il valore restituito da una funzione). Non sono nemmeno esperto sulle decisioni di progettazione, ma penso che una risorsa conteggiata di riferimento sia un esempio in cui il costruttore di mosse potrebbe causare problemi. Modificherò la mia risposta e includerò l'esempio che avevo in mente quando ho scritto su "problemi di sincronizzazione". –

1

Il modello di istanza di atomic<bool> appare essenzialmente come questo:

struct atomic<bool> 
{ 
    atomic<bool>(bool); 
    atomic<bool>(const atomic<bool>&) = delete; 
    operator bool() const; 
} 

Così, quando si tenta di copiarlo:

atomic<bool> a = ...; 
atomic<bool> b(a); 

Il costruttore di copia eliminato viene scelto e causa un errore di compilazione.

È necessario lanciare esplicitamente di bool di passare attraverso operator bool() --> atomic<bool>(bool) ...

atomic<bool> a = ...; 
atomic<bool> b(bool(a)); 
+1

Non è più sicuro usare static_cast (facendo riferimento all'ultima riga di codice nella soluzione)? –