2013-08-21 22 views
14

Zauważyłem, że nie można przekazać referencji niestanowiących stałych jako argumentu do std::async.Przekazywanie argumentów do std :: async przez odniesienie kończy się niepowodzeniem.

#include <functional> 
#include <future> 

void foo(int& value) {} 

int main() { 
    int value = 23; 
    std::async(foo, value); 
} 

Mój kompilator (GCC 4.8.1) daje następujący błąd w tym przykładzie:

error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’ 

Ale gdybym owinąć wartości przekazanej std::async w std::reference_wrapper, wszystko jest OK. Zakładam, że dzieje się tak, ponieważ std::async przyjmuje argumenty według wartości, ale nadal nie rozumiem przyczyny błędu.

+3

Nie trzeba wpisywać 'std :: reference_wrapper'. Użyj 'std :: ref'. – chris

+0

Używam 'std :: ref', ale dzięki za podpowiedź :) – lizarisk

+0

Będziesz musiał wykopać kod źródłowy libstdC++, jeśli interesuje Cię przyczyna tego błędu: http: //gcc.gnu .org/onlinedocs/gcc-4.8.1/libstdC++/api/a01256_source.html Numery wierszy do obejrzenia znajdują się w błędach. Powodzenia. – jrok

Odpowiedz

24

Jest to przemyślany wybór/kompromis.

Po pierwsze, nie jest konieczne, aby dowiedzieć się, czy funkcja ta przekazana do async przyjmuje swoje argumenty przez odniesienie, czy też nie. (Jeśli nie jest to prosta funkcja, ale obiekt funkcji, może to być na przykład przeciążony operator wywołania funkcji). Tak więc async nie może powiedzieć: "Hej, pozwól mi tylko sprawdzić, co chce funkcja docelowa, a ja postąpię właściwie rzecz."

Zatem pytanie projektowe brzmi, czy w miarę możliwości bierze pod uwagę wszystkie argumenty (np. Czy mają one wartość l), czy zawsze wykonuje kopie? Wykonanie kopii jest tutaj wyborem: kopia nie może się zwinąć, a kopia nie może przedstawiać warunków wyścigu (chyba, że ​​jest naprawdę dziwna). Tak więc dokonano wyboru: wszystkie argumenty są domyślnie kopiowane.

Ale wtedy mechanizm jest zapisywany tak, że faktycznie nie przekazuje następnie argumentów do stałego parametru odniesienia o stałej wartości l. To kolejny wybór dla bezpieczeństwa: w przeciwnym razie funkcja, która może zmienić oryginalną lwartość modyfikuje kopię, prowadząc do błędów, które są bardzo trudne do wyśledzenia.

Ale co, jeśli naprawdę, naprawdę chcesz mieć stały parametr odniesienia o wartości l? Co, jeśli obiecasz uważać na zwisające referencje i warunki wyścigu? Właśnie do tego służy std :: ref. Jest to jednoznaczne wyrażenie zgody na niebezpieczną semantykę odniesienia. To twój sposób na powiedzenie: "Wiem, co tu robię".

15

std::async (i inne funkcje umożliwiające doskonałe przekazywanie) sprawdź typ przekazywanego argumentu, aby dowiedzieć się, co należy zrobić. Nie patrzą, w jaki sposób ten argument zostanie ostatecznie użyty. Aby przekazać obiekt przez odniesienie, musisz powiedzieć std::async, że używasz odniesienia. Jednak samo podanie odniesienia nie spowoduje tego. Musisz użyć numeru std::ref(value), aby przekazać value przez odniesienie.

+0

Bardzo jasna odpowiedź. – Sheen

14

Sam problem jest tylko w niewielkim stopniu związane std::async() Przy ustalaniu wyniku operacji std::async() wykorzystuje std::result_of<...>::type ze wszystkimi jego argumenty są std::decay<...>::type „wyd. Jest to uzasadnione, ponieważ std::async() przyjmuje typy arbitralne i przekazuje je do przechowywania w niektórych lokalizacjach. Aby je zapisać, potrzebne są wartości zarówno dla obiektu funkcji, jak i dla argumentów. Zatem std::result_of<...> stosowany jest podobny do tego:

typedef std::result_of<void (*(int))(int&)>::type result_type; 

... a ponieważ int nie może być związany z int& (int nie jest typem lwartość było potrzebne, aby być związany int&), to się nie powiedzie . Niepowodzenie w tym przypadku oznacza, że ​​std::result_of<...> nie definiuje zagnieżdżonego .

Kontynuacją pytanie mogłoby być: co to jest typ używany do wystąpienia std::result_of<...>? Idea jest taka, że ​​składnia wywołania funkcji składająca się z ResultType(ArgumentTypes...) jest nadużywana: zamiast typu wyniku przekazywany jest typ funkcji, a std::result_of<...> określa typ funkcji wywoływanej, gdy wywoływany jest ten typ funkcji z podaną listą argumentów. Dla typów wskaźników funkcji nie jest to naprawdę interesujące, ale typ funkcji może być również obiektem funkcji, w którym należy wziąć pod uwagę przeciążanie. Zasadniczo, std::result_of<...> jest używany w następujący sposób:

typedef void (*function_type)(int&); 
typedef std::result_of<function_type(int)>::type result_type; // fails 
typedef std::result_of<function_type(std::reference_wrapper<int>)>::type result_type; //OK 
+1

+1 IMHO ten post jest tym, który * naprawdę * analizuje komunikat o błędzie pokazany w OP. –

Powiązane problemy