Se você deseja desempenho, passe por valor se estiver armazenando.
Suponha que você tenha uma função chamada "execute isso no thread da interface do usuário".
std::future<void> run_in_ui_thread( std::function<void()> )
que executa algum código no thread "ui" e sinaliza future
quando terminar. (Útil em estruturas de interface do usuário em que o segmento da interface do usuário é onde você deve mexer com os elementos da interface do usuário)
Temos duas assinaturas que estamos considerando:
std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)
Agora, é provável que as usemos da seguinte maneira:
run_in_ui_thread( [=]{
// code goes here
} ).wait();
que criará um fechamento anônimo (um lambda), constrói um a std::function
partir dele, passa-o para a run_in_ui_thread
função e aguarda a conclusão da execução no encadeamento principal.
No caso (A), o std::function
é construído diretamente a partir do nosso lambda, que é então usado dentro do run_in_ui_thread
. O lambda é move
d dentro do std::function
, portanto, qualquer estado móvel é eficientemente transportado para ele.
No segundo caso, um temporário std::function
é criado, o lambda é move
d, e esse temporário std::function
é usado por referência dentro do run_in_ui_thread
.
Até agora, tudo bem - os dois têm desempenho idêntico. Exceto run_in_ui_thread
que ele fará uma cópia do seu argumento de função para enviar ao thread da interface do usuário para executar! (ele retornará antes de terminar, portanto, não pode apenas usar uma referência a ele). Para o caso (A), nós simplesmente move
o std::function
em seu armazenamento a longo prazo. No caso (B), somos forçados a copiar o arquivo std::function
.
Essa loja torna a passagem por valor mais ideal. Se houver alguma possibilidade de você estar armazenando uma cópia do std::function
, passe por valor. Caso contrário, qualquer uma das maneiras é aproximadamente equivalente: a única desvantagem do valor é se você estiver usando o mesmo volume std::function
e tendo um sub método após o outro. Salvo isso, a move
será tão eficiente quanto a const&
.
Agora, existem outras diferenças entre as duas que mais se destacam se tivermos um estado persistente dentro da std::function
.
Suponha que o std::function
objeto seja armazenado com a operator() const
, mas também possui alguns mutable
membros de dados que ele modifica (que rude!).
No std::function<> const&
caso, os mutable
membros de dados modificados serão propagados para fora da chamada de função. No std::function<>
caso, eles não vão.
Este é um caso de canto relativamente estranho.
Você quer tratar std::function
como faria com qualquer outro tipo móvel, possivelmente pesado e barato. Mover é barato, copiar pode ser caro.
sizeof(std::function)
mais2 * sizeof(size_t)
, que é o menor tamanho que você consideraria para uma referência const.