examples c++ templates lambda c++11

lambda examples c++



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

Cuando defino esta función,

template<class A> set<A> test(const set<A>& 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<class A> set<A> filter(const set<A>& input,function<bool(A)> compare) { set<A> 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<bool(int)> func = [](int i) { return i%2 ==0; }; set<int> myNewSet = filter(mySet,func); set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; }); set<int> myNewSet = filter(mySet,function<bool(int)>([](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<class A,class CompareFunction> set<A> filter(const set<A>& input,CompareFunction compare) { set<A> ret; for(auto it = input.begin(); it != input.end(); it++) { if(compare(*it)) { ret.insert(*it); } } return ret; }

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

El compilador 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 sintaxis 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<class Value,class CompareType,class IndexType> auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> { map<decltype(index(*(input.begin()))),Value> 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<string,int> 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 compilador 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 compilador 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 compilador 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<typename T> struct X { X(T data) {} }; template<typename T> void f(X<T> 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<typename T> 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<typename T> struct X<T*> //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 compilador (es decir, son funtores de diferentes tipos que std::function ).