C ++ 11 no deduce el tipo cuando las funciones std :: function o lambda están involucradas

Cuando defino esta función,

template set test(const set& input) { return input; } 

Puedo llamarlo usando test(mySet) en otro lugar del código sin tener que definir explícitamente el tipo de plantilla. Sin embargo, cuando uso la siguiente función:

 template set filter(const set& input,function compare) { set ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

Cuando llamo a esta función usando filter(mySet,[](int i) { return i%2==0; }); Obtuve el siguiente error:

error: no hay función de coincidencia para llamar a ‘filter (std :: set &, main (): :)’

Sin embargo, todas estas versiones funcionan:

 std::function func = [](int i) { return i%2 ==0; }; set myNewSet = filter(mySet,func); set myNewSet = filter(mySet,[](int i) { return i%2==0; }); set myNewSet = filter(mySet,function([](int i){return i%2==0;})); 

¿Por qué c ++ 11 no puede adivinar el tipo de plantilla cuando pongo la función lambda directamente dentro de la expresión sin crear directamente una std::function ?

EDITAR:

Por consejo de Luc Danton en los comentarios, aquí hay una alternativa a la función que tenía antes que no necesita que las plantillas se pasen explícitamente.

 template set filter(const set& input,CompareFunction compare) { set ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; } 

Esto se puede llamar mediante set result = filter(myIntSet,[](int i) { i % 2 == 0; }); sin necesidad de la plantilla.

El comstackdor incluso puede adivinar los tipos de retorno en cierta medida, utilizando la nueva palabra clave decltype y utilizando la nueva función de tipo de syntax de retorno. Aquí hay un ejemplo que convierte un conjunto en un mapa, usando una función de filtrado y una función que genera las claves basadas en los valores:

 template auto filter(const set& input,CompareType compare,IndexType index) -> map { map ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret[index(*it)] = *it; } } return ret; } 

También se puede invocar sin usar la plantilla directamente, ya que

 map s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); }); 

El problema es sobre la naturaleza de lambdas. Son objetos funcionales con un conjunto fijo de propiedades según el estándar, pero no son una función. El estándar determina que lambdas se puede convertir en std::function<> con los tipos exactos de argumentos y, si no tienen estado, funcionan con punteros.

Pero eso no significa que una lambda sea una std::function ni un puntero a la función. Son tipos únicos que implementan operator() .

La deducción de tipo, por otro lado, solo deducirá los tipos exactos, sin conversiones (aparte de las calificaciones const / volátiles). Como lambda no es una std::function el comstackdor no puede deducir el tipo en la llamada: filter(mySet,[](int i) { return i%2==0; }); ser cualquier instanciación std::function<> .

A partir de los otros ejemplos, en el primero convierte el lambda al tipo de función, y luego lo pasa. El comstackdor puede deducir el tipo allí, como en el tercer ejemplo donde la std::function es un valor r (temporal) del mismo tipo.

Si proporciona el tipo de int instancias int en la plantilla, segundo ejemplo de trabajo, la deducción no entra en juego, el comstackdor usará el tipo y luego convertirá el lambda al tipo apropiado.

Olvídate de tu caso. ya que eso es demasiado complejo para el análisis.

Toma este simple ejemplo:

  template struct X { X(T data) {} }; template void f(X x) {} 

Ahora llama f como:

  f(10); 

Aquí podría sentirse tentado a pensar que T se deduce a int y , por lo tanto, la llamada a la función anterior debería funcionar. Bueno, ese no es el caso. Para mantener las cosas simples, imagina que hay otro constructor que toma int como:

  template struct X { X(T data) {} X(int data) {} //another constructor }; 

Ahora, ¿a qué debería deducirse T cuando escribo f(10) ? Bueno, T podría cualquier tipo.

Tenga en cuenta que podría haber muchos otros casos similares. Tome esta especialización, por ejemplo:

  template struct X //specialized for pointers { X(int data) {}; }; 

Ahora, ¿qué T debería deducirse para la llamada f(10) ? Ahora parece aún más difícil.

Por lo tanto, es un contexto no deducible, lo que explica por qué su código no funciona para std::function que es un caso idéntico, simplemente se ve complejo en la superficie. Tenga en cuenta que las lambdas no son de tipo std::function ; básicamente son instancias de clases generadas por el comstackdor (es decir, son funtores de diferentes tipos que std::function ).