Considere este código ( godbolt ):
#include <iostream>
template<typename F> void call_by_val(F funct)
{
std::cout << "call_by_val(): ";
funct();
}
template<typename F> void call_by_ref(F& funct)
{
std::cout << "call_by_ref(): ";
funct();
}
template<typename F> void call_by_cref(const F& funct)
{
std::cout << "call_by_cref(): ";
funct();
}
void free_funct()
{
std::cout << "free_funct()\n";
}
int main()
{
call_by_val( free_funct );
call_by_ref( free_funct );
call_by_cref( free_funct );
}
Todas essas três chamadas funcionam como esperado. No caso de uma função livre, não tenho certeza do que está acontecendo por baixo do tapete, então estou pensando qual é a diferença entre esses três estilos, semanticamente falando. Existe uma razão objetiva para preferir um em vez dos outros?
É impossível passar funções por valor. Quando um parâmetro de uma função tem um tipo de função, ele é ajustado automaticamente para "ponteiro para [o tipo de função]". Similarmente, a dedução decairá o argumento do tipo de função para um ponteiro de função antes da dedução se o parâmetro da função for de um tipo não referencial.
Então,
F
na primeira chamada serávoid(*)()
, nãovoid()
.Na referência as sobrecargas
F
serão deduzidas paravoid()
.const
não tem significado para referências a funções e, portanto, o parâmetro na segunda e na terceira chamada será do tipovoid(&)()
.Se
funct
é uma referência-para-função ou um ponteiro-para-função, não importa muito, porque você pode chamar qualquer um como se tivesse nomeado a função diretamente. Não há diferença no significado, exceto o quedecltype(funct)
produziria.