2013-01-06 7 views
23

Mam klasę o zmiennej składowej atomowej:C++ 11: Napisz konstruktora ruchu z atomowym członkiem <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 

Jak powinna wyglądać przenieść konstruktora?

Gcc 4.7 nie lubi żadnego z moich prób (takich jak dodawanie std::move() wokół other.bar) i netto jest zaskakująco cichy tutaj ...

Odpowiedz

14

Od ruszasz other, nikt inny nie będzie do niego dostęp. Czytanie z jego bar jest bezpieczne, ale czy to atomowe, czy nie.

atomic<T> ma tylko dwa konstruktory, z których jeden to domyślny (), drugi to (T). Twój kod wygląda na to, że powinien się skompilować. Jeśli tak nie jest, co się stanie, jeśli static_cast other.bar do T, wymuszając użycie konstruktora (T)?

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

lub jest równa, a prawdopodobnie mniej brzydki:

: bar(other.bar.load())

+1

Dzięki, 'bar (other.bar.load())' jest właściwym rozwiązaniem, które jest kompilacji teraz! – Chris

+7

_So, Twój kod wygląda na to, że powinien się skompilować. Nie, 'atomowy ' ma konstruktora skasowanego kopiowania, a rozdzielczość przeciążenia stwierdza, że ​​nie jest to konstruktor 'atomowy (T)'. Odlewanie lub ładunek są niezbędne. –

+0

@ Jonathan Masz rację, myślałem, że kompilator przejdzie do następnego możliwego konstruktora '(T)', jeśli wie, że konstruktor kopiowania został usunięty i istnieje 'operator T'. Czy powodem tego jest to, że konstruktorem atomowym dla '(T)' jest 'constexpr' sprawiający, że zachowuje się on podobnie do' jawnych' konstruktorów, że niejawne konwersje muszą być jawne (lub w tym przypadku 'constexpr')? –

22

std::atomic nie copyable lub ruchome, gdyż its copy constructor is deleted i konstruktor ruch nie jest zdefiniowana. Musisz jawnie załadować drugą wartość i użyć jej do skonstruowania nowej wartości, jak wskazano w odpowiedzi od gustafa.

Dlaczego std::atomic nie jest ruchoma? Ponieważ jest to prymityw synchronizacji, wszystkie wątki muszą być synchronizowane na tych samych danych (tj. Ten sam adres). Kiedy kopiujesz (lub przesuwasz) wartość atomową, musisz użyć jakiegoś protokołu komunikacyjnego. Może to być proste, jak w twoim przykładzie (wystarczy go załadować i użyć do zainicjowania nowego atomowego), ale generalnie myślę, że to dobra decyzja projektowa C++ 11, aby zmusić cię do przemyślenia tego. W przeciwnym razie może to skutkować kodem, który wygląda dobrze, ale ma pewne problemy z synchronizacją subtelną.

+1

Próbuję wymyślić sytuację, w której może być problematyczne, aby mieć atomową automatycznie przenoszone, gdy masz wiele wątków. Ręczne wymuszanie ruchu za pomocą std :: move() to kolejna rzecz. Tak czy inaczej, chciałbym podać przykład pokazujący, gdzie poruszanie atomem byłoby problematyczne. Możesz przenieść dowolny inny kontener 'std' i oczekuje się, że zajmie się synchronizacją. –

+1

Chciałbym również wiedzieć, czy nie byłoby złym pomysłem, gdyby 'atomowy' miał konstruktor ruchu, który musiałby zostać wywołany przez jawne wywołanie' std :: move() '- dałby mi interfejs, który Spodziewałbym się. Ale jestem zbyt daleko od bycia ekspertem C++, aby ocenić, czy ta decyzja dotycząca standardu ma cel, czy jest błędem ... – Chris

+1

Konstruktor ruchu nie może zdecydować, czy wartość renu wynikała z jawnego 'std :: move' lub jest niejawnie rwartą (np. wartość zwracana przez funkcję). Nie jestem też ekspertem od decyzji projektowych, ale myślę, że zasób z licznikiem referencyjnym jest przykładem, w którym konstruktor ruchu może prowadzić do problemów. Zmienię moją odpowiedź i uwzględnię przykład, który miałem na myśli, gdy pisałem o "problemach z synchronizacją". –

1

Szablon instancji z atomic<bool> zasadniczo wygląda następująco:

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

Więc kiedy spróbujesz je skopiować:

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

Usunięta konstruktor kopia jest wybrany i powoduje błąd kompilacji.

Trzeba wyraźnie rzutować bool przejść operator bool() --> atomic<bool>(bool) ...

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

Czy nie jest bezpieczniej używać static_cast (w odniesieniu do ostatniej linii kodu w swoim rozwiązaniu)? –

Powiązane problemy