¿Por qué el siguiente código no compila? ¿Por qué tengo que decirle al compilador que el puntero de función pasado devuelve un doble? (¡Funciona si uno llama explícitamente a call<double>() !)
template<typename T> void call(T (*const _pF)(void)) { } int main(int, char**) { call( [](void) -> double { return 1.0; } ); }Debido a que la conversión implícita (de lambda a puntero de función) no se considerará en la deducción del argumento de la plantilla, el parámetro T de la plantilla no se puede deducir y la invocación falla.
La deducción de tipo no tiene en cuenta las conversiones implícitas (aparte de los ajustes de tipo enumerados anteriormente): ese es el trabajo para la resolución de sobrecarga, que sucede más adelante.
Excepto por especificar el argumento de la plantilla double explícitamente como call<double>(...); , puede convertir la lambda en un puntero de función explícitamente, por ejemplo
call( static_cast<double(*)()>( [](void) -> double { return 1.0; }) );o
call( +[](void) -> double { return 1.0; } );No pasó la lambda a su función de call , pero creó implícitamente un puntero de función. Eso hace que su función de llamada solo funcione con lambdas sin captura. Si eso está bien, ¡bien!
¡Pero puede simplificar todo aceptando el tipo lambda en sí mismo!
template<typename T> auto call(T &&f) { return f(); } int main(int, char**) { std::cout << call( [](){ return 1.0; } ) << std::endl; } Tampoco es necesario especificar el valor de retorno de lambda ya que 1.0 automáticamente convierte el tipo de retorno de lambda en double .
Como puede ver, también se puede deducir el tipo de devolución de la call y, como resultado, puede usar directamente la función de llamada para devolver el resultado mismo a algo.
Pero todo esto también tiene un inconveniente: para cada lambda, obtiene una nueva instancia de plantilla de llamada.