2012-04-03 12 views
9

Następujący program nie tworzy w VS11 beta GCC 4,5 lub brzękiem 3,1std :: gwint z ruchomym, nie copyable argumentu

#include <thread> 
#include <memory> 

int main() { 
    std::unique_ptr<int> p; 
    std::thread th([](std::unique_ptr<int>) { 

    },std::move(p)); 
    th.join(); 
} 

To dlatego, że typ argumentu jest copyable, ale Próby implementacji do skopiowania.

O ile mi wiadomo, program ten jest dobrze sformułowany i powinien działać. Wymagania std :: thread wydają się sugerować, że powinny tu działać ruchome, niekopiowujące argumenty. Mówi konkretnie, że obiekt wywoływalny i każdy argument musi spełniać wymagania MoveConstructible, a wyrażenie INVOKE(DECAY_COPY(std::forward<F>(f)),DECAY_COPY(std::forward<Args>(args))...) powinno być poprawnym wyrażeniem.

W tym przypadku myślę, że wyrażenie przekłada się na coś takiego:

template <class T> typename std::decay<T>::type decay_copy(T&& v) 
{ return std::forward<T>(v); } 

std::unique_ptr<int> p; 
auto f = [](std::unique_ptr<int>) {}; 

decay_copy(f)(decay_copy(std::move(p))); 

I nie sądzę, że to ma dotyczyć kopię p. gcc przynajmniej może skompilować to wyrażenie, chociaż VS11 tego nie robi.

  1. Czy mam rację co do wymagań, a argumenty muszą być możliwe do skopiowania?
  2. Czy standard pozostawia swobodę w tej kwestii dla implementacji do kopiowania argumentów?
  3. Czy implementacja, której próbowałem, nie jest zgodna?
+0

Wygląda na to, że przekazujesz argument wątku przez kopiowanie (jak na anonimowy podpis funkcji). Czy argumentem nie powinien być 'std :: unique_ptr &&' lub' const std :: unique_ptr & '? –

+2

@ André: Nie ma czegoś takiego jak przekazywanie kopii; przekazanie argumentu przez _value_ spowoduje skopiowanie lub przesunięcie w zależności od tego, czy wywołujący przejdzie wartość l, czy rwartość. – ildjarn

+1

@ildjarn: Przepraszam, miałem na myśli "według wartości", a nie "według kopii". Pomyślałem, że przekazanie argumentów według wartości spowoduje wybór konstruktora ruchu, o ile jest dostępny. –

Odpowiedz

14

Od 30.3.1.2, pkt 3 i 4 N3337:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

Wymaga: F i każdy Ti w Args spełniają MoveConstructible wymagań. INVOKE (DECAY_-COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2) będzie prawidłowym wyrażeniem.

Efekty: Konstruuje obiekt typu wątku. Nowy wątek wykonywania wykonuje INVOKE (DECAY_-COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...), a wywołania DECAY_COPY są analizowane w wątku konstrukcyjnym. Każda wartość zwracana z tego wywołania jest ignorowana. [Uwaga: Oznacza to, że w wątku konstrukcyjnym zostaną rzucone wszystkie wyjątki, które nie zostały wyrzucone z wywołania egzemplarza f, a nie nowy wątek. -end note] Jeśli wywołanie INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) kończy się nieprzechwyconym wyjątkiem, wywołanie std :: terminate zostanie wywołane.

Tak, to powinno zadziałać. Jeśli nie, jest to błąd w twojej implementacji.

Należy pamiętać, że każdy ruch/kopiowanie parametru nastąpi w nowym wątku. Przekazujesz odniesienia do innego wątku, więc musisz upewnić się, że one istnieją, dopóki ten wątek się nie rozpocznie.

+2

I działa z g ++, od wersji 4.7 – je4d

+0

A teraz nie mogę już odtworzyć błędu w klangu, nawet jeśli kod użyty wcześniej jest w repozytorium źródłowym i mam dokładną linię poleceń w mojej historii. Chyba powinienem ponownie sprawdzić vs11. – bames53

+1

Wygląda na to, że problem dotyczy starej wersji biblioteki libC++ i najnowszej. – bames53

3

Jako alternatywa, jak i standardowej std::thread idiomu, można przekazać owijkę odniesienie:

int p; 
std::thread([](int & x) { /* ... */ }, std::ref(p)); 

Stwarza to obiekt typu std::reference_wrapper<int>, który ma semantykę wartości i zawija odwołanie do int (tj skopiowanie opakowania powoduje aliasy odwołań).

Powiązane problemy