¿Por qué las funciones lambda en c ++ 11 no tienen función tipos?

Estoy jugando con las características funcionales de c ++ 11. Una cosa que encuentro extraña es que el tipo de una función lambda en realidad NO es un tipo de función . Además, las lambda no parecen jugar muy bien con el mecanismo de tipografía.

Attached es un pequeño ejemplo en el que probé volteando los dos argumentos de una función para agregar dos enteros. (El comstackdor que utilicé fue gcc 4.6.2 en MinGW). En el ejemplo, el tipo para addInt_f se ha definido explícitamente con la función , mientras que addInt_l es una lambda cuyo tipo está addInt_l tipo con auto .

Cuando compilé el código, la función flip puede aceptar la versión explícitamente definida por tipo de addInt pero no la versión lambda, dando un error al decir que, testCppBind.cpp:15:27: error: no matching function for call to 'flip(&)'

Las siguientes líneas muestran que la versión lambda (así como una versión “en bruto”) se puede aceptar si se envía explícitamente al tipo de función adecuado.

Entonces mis preguntas son:

  1. ¿Por qué es que una función lambda no tiene una function tipo en primer lugar? En el pequeño ejemplo, ¿por qué addInt_l no tiene la function como tipo en lugar de tener un tipo lambda diferente? Desde la perspectiva de la progtwigción funcional, ¿cuál es la diferencia entre una función / objeto funcional y una lambda?

  2. Si hay una razón fundamental para que estos dos tengan que ser diferentes. Escuché que las lambda pueden convertirse a function pero son diferentes. ¿Es este un problema de diseño / defecto de C ++ 11, un problema de implementación o hay un beneficio en distinguir los dos tal como es? Parece que la firma de tipo de addInt_l solo ha proporcionado suficiente información sobre el parámetro y los tipos de devolución de la función.

  3. ¿Hay alguna manera de escribir el lambda para evitar el colado de tipos explícito mencionado anteriormente?

Gracias por adelantado.

  //-- testCppBind.cpp -- #include  using namespace std; using namespace std::placeholders; template  function flip(function f) { return bind(f,_2,_1);} function addInt_f = [](int a,int b) -> int { return a + b;}; auto addInt_l = [](int a,int b) -> int { return a + b;}; int addInt0(int a, int b) { return a+b;} int main() { auto ff = flip(addInt_f); //ok auto ff1 = flip(addInt_l); //not ok auto ff2 = flip((function)addInt_l); //ok auto ff3 = flip((function)addInt0); //ok return 0; } 

std::function es una herramienta útil para almacenar cualquier tipo de objeto invocable, independientemente de su tipo. Para hacer esto, necesita emplear algún tipo de técnica de borrado, y eso implica un poco de sobrecarga.

Cualquier llamable se puede convertir implícitamente a una std::function , y es por eso que generalmente funciona a la perfección.

Voy a repetir para asegurarme de que quede claro: std::function no es algo solo para lambdas o punteros a funciones: es para cualquier tipo de invocación. Eso incluye cosas como struct some_callable { void operator()() {} }; , por ejemplo. Eso es simple, pero podría ser algo como esto en su lugar:

 struct some_polymorphic_callable { template  void operator()(T); }; 

Una lambda es simplemente otro objeto invocable, similar a las instancias del objeto some_callable anterior. Se puede almacenar en una std::function porque es invocable, pero no tiene la tara de borrado de tipo de std::function .

Y el comité planea hacer lambdas polimórficos en el futuro, es decir, lambdas que se ven como some_polymorphic_callable arriba. ¿Qué tipo de std::function sería una lambda?


Ahora … Deducción del parámetro de la plantilla, o conversiones implícitas. Elegir uno. Esa es una regla de plantillas C ++.

Para pasar un argumento lambda como std::function , debe convertirse implícitamente. Tomar un argumento std::function significa que está eligiendo conversiones implícitas sobre la deducción de tipo. Pero su plantilla de función necesita que la firma se deduzca o proporcione explícitamente.

¿La solución? No restrinja las llamadas a std::function . Acepta cualquier tipo de llamamiento .

 template  auto flip(Fun&& f) -> decltype(std::bind(std::forward(f),_2,_1)) { return std::bind(std::forward(f),_2,_1); } 

Ahora puede estar pensando por qué necesitamos std::function entonces. std::function proporciona borrado de tipo para callables con una firma conocida. Eso esencialmente hace que sea útil para almacenar callables borrados por tipo y para escribir interfaces virtual .

  1. Porque la function<> emplea borrado de tipo . Esto permite almacenar varios tipos de funciones diferentes en una function<> , pero incurre en una pequeña penalización de tiempo de ejecución. La borradura de tipo oculta el tipo real (su lambda específica) detrás de una interfaz de función virtual.
  2. Hay un beneficio en esto: uno de los “axiomas” de diseño de C ++ es nunca agregar sobrecarga a menos que realmente se necesite. Al utilizar esta configuración, no tiene ningún costo indirecto al usar la inferencia de tipo (use auto o pase como un parámetro de plantilla), pero todavía tiene toda la flexibilidad para interactuar con código que no sea de plantilla a través de la function<> . También tenga en cuenta que la function<> no es una construcción de lenguaje, sino un componente de la biblioteca estándar que puede implementarse utilizando funciones de lenguaje simples.
  3. No, pero puede escribir la función para simplemente tomar el tipo de la función (construcción del lenguaje) en lugar de los detalles de la function<> (construcción de la biblioteca). Por supuesto, eso hace que sea mucho más difícil escribir el tipo de devolución, ya que no proporciona directamente los tipos de parámetros. Sin embargo, al usar meta-progtwigción a la Boost.FunctionTypes , puede deducirlos de la función que pasa. Sin embargo, en algunos casos esto no es posible, por ejemplo, con funtores que tienen un operator() plantilla operator() .