Jeśli chcesz wydajności, podaj wartość, jeśli ją przechowujesz.
Załóżmy, że masz funkcję o nazwie „uruchom to w wątku interfejsu użytkownika”.
std::future<void> run_in_ui_thread( std::function<void()> )
który uruchamia kod w wątku „ui”, a następnie sygnalizuje zakończenie future. (Przydatne w strukturach UI, w których wątek UI jest miejscem, w którym powinieneś zadzierać z elementami UI)
Rozważamy dwa podpisy:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Teraz prawdopodobnie użyjemy ich w następujący sposób:
run_in_ui_thread( [=]{
// code goes here
} ).wait();
co utworzy anonimowe zamknięcie (lambda), skonstruuje z niego std::functionwyjście, przekaże je do run_in_ui_threadfunkcji, a następnie zaczeka, aż zakończy się działanie w głównym wątku.
W przypadku (A), std::functionjest on konstruowany bezpośrednio z naszej lambdy, która jest następnie używana w run_in_ui_thread. Lambda jest moved do std::function, więc każdy ruchomy stan jest skutecznie przenoszony do niej.
W drugim przypadku std::functiontworzony jest tymczasowy , lambda jest movew nim d, a następnie ten tymczasowy std::functionjest używany przez odniesienie w run_in_ui_thread.
Jak na razie dobrze - obaj grają identycznie. Tyle run_in_ui_threadże program utworzy kopię swojego argumentu funkcji do wysłania do wątku interfejsu użytkownika w celu wykonania! (wróci, zanim zostanie z nim zakończony, więc nie może po prostu użyć odniesienia do niego). W przypadku (A), po prostu do jego długotrwałego przechowywania. W przypadku (B) jesteśmy zmuszeni skopiować plik .movestd::functionstd::function
Ten sklep sprawia, że przekazywanie wartości jest bardziej optymalne. Jeśli istnieje możliwość, że przechowujesz kopię pliku std::function, podaj wartość. W przeciwnym razie obie metody są z grubsza równoważne: jedyną wadą wartości bocznej jest to, że bierzesz tę samą masę std::functioni jedną metodę podrzędną po drugiej. Poza tym, a movebędzie tak samo wydajne jak const&.
Teraz są pewne inne różnice między tymi dwoma, które najczęściej pojawiają się, jeśli mamy trwały stan w obrębie std::function.
Załóżmy, że std::functionprzechowuje jakiś obiekt z a operator() const, ale ma też kilka mutableczłonków danych, które modyfikuje (jakie niegrzeczne!).
W takim std::function<> const&przypadku mutablezmodyfikowane elementy składowe danych będą propagowane poza wywołanie funkcji. W takim std::function<>przypadku nie będą.
To stosunkowo dziwny przypadek narożny.
Chcesz traktować std::functionjak każdy inny potencjalnie ciężki, tani przenośny typ. Przenoszenie jest tanie, kopiowanie może być kosztowne.
sizeof(std::function)się, że nie będzie więcej niż2 * sizeof(size_t), czyli najmniejszy rozmiar, jaki kiedykolwiek wziąłbyś pod uwagę jako odniesienie do stałej.