examples c++ templates lambda c++11

c++ - examples - Expresiones Lambda como parámetros de plantilla de clase



lambda examples c++ (4)

¿Se pueden usar expresiones lambda como parámetros de plantilla de clase? (Tenga en cuenta que esta es una pregunta muy diferente a esta , que pregunta si una expresión lambda puede ser templada).

Te estoy preguntando si puedes hacer algo como:

template <class Functor> struct Foo { }; // ... Foo<decltype([]()->void { })> foo;

Esto sería útil en casos donde, por ejemplo, una plantilla de clase tiene varios parámetros como equal_to o algo, que generalmente se implementan como functors de una sola línea. Por ejemplo, supongamos que quiero crear una instancia de una tabla hash que utiliza mi propia función de comparación de igualdad personalizada. Me gustaría poder decir algo como:

typedef std::unordered_map< std::string, std::string, std::hash<std::string>, decltype([](const std::string& s1, const std::string& s2)->bool { /* Custom implementation of equal_to */ }) > map_type;

Pero probé esto en GCC 4.4 y 4.6, y no funciona, aparentemente porque el tipo anónimo creado por una expresión lambda no tiene un constructor predeterminado. (Recuerdo un problema similar con boost::bind .) ¿Hay alguna razón por la cual el borrador del estándar no permite esto, o estoy equivocado y está permitido, pero GCC está detrás en su implementación?


Te estoy preguntando si puedes hacer algo como:

Foo<decltype([]()->void { })> foo;

No, no se puede, porque las expresiones lambda no aparecerán en un contexto no evaluado (como decltype y sizeof , entre otros). C ++ 0x FDIS, 5.1.2 [expr.prim.lambda] p2

La evaluación de una expresión lambda da como resultado un prvalue temporal (12.2). Este temporal se llama el objeto de cierre. Una expresión lambda no aparecerá en un operando no evaluado (Cláusula 5). [Nota: un objeto de cierre se comporta como un objeto de función (20.8) .- nota final] (énfasis mío)

Tendría que crear primero un lambda específico y luego usar decltype en eso:

auto my_comp = [](const std::string& left, const std::string& right) -> bool { // whatever } typedef std::unordered_map< std::string, std::string, std::hash<std::string>, decltype(my_comp) > map_type;

Esto se debe a que cada objeto de cierre derivado de lambda podría tener un tipo completamente diferente; después de todo, son como funciones anónimas .


@Xeo te dio el motivo, así que te daré el trabajo.

Muchas veces no desea nombrar un cierre, en este caso, puede usar std::function , que es un tipo:

typedef std::unordered_map< std::string, std::string, std::hash<std::string>, std::function<bool(std::string const&, std::string const&)> > map_type;

Tenga en cuenta que captura exactamente la firma de la función, y no más.

Entonces, simplemente escriba la lambda cuando construya el mapa.

Tenga en cuenta que con unordered_map , si cambia la comparación de igualdad, será mejor que cambie el hash para que coincida con el comportamiento. Los objetos que se comparan iguales tendrán el mismo hash.


No puede hacer esto con un cierre, porque el estado no está incluido en el tipo.

Si tu lambda es apátrida (sin capturas), entonces deberías estar bien. En este caso, la lambda se descompone en un puntero de función normal, que puede usar como un argumento de plantilla en lugar de un tipo de lambda.

A gcc no le gusta, sin embargo. http://ideone.com/bHM3n


Tendrá que usar un tipo de resumen en tiempo de ejecución, como std::function , o crear el tipo como una variable local o como parte de una clase de plantilla.