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 futurequando 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::functionpartir dele, passa-o para a run_in_ui_threadfunçã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 é moved dentro do std::function, portanto, qualquer estado móvel é eficientemente transportado para ele.
No segundo caso, um temporário std::functioné criado, o lambda é moved, 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_threadque 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 moveo std::functionem 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::functione tendo um sub método após o outro. Salvo isso, a moveserá 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::functionobjeto seja armazenado com a operator() const, mas também possui alguns mutablemembros de dados que ele modifica (que rude!).
No std::function<> const&caso, os mutablemembros 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::functioncomo 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.