¿Cuál es el tipo de lambda cuando se deduce con "auto" en C ++ 11?


142

Tenía la percepción de que el tipo de lambda es un puntero de función. Cuando realicé la siguiente prueba, encontré que estaba equivocado ( demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

¿Al código anterior le falta algún punto? Si no es así, ¿cuál es la typeofexpresión lambda cuando se deduce con la autopalabra clave?


8
“El tipo de lambda es un puntero de función”, eso sería ineficiente y perdería todo el punto de las lambdas.
Konrad Rudolph el

Respuestas:


145

El tipo de una expresión lambda no está especificado.

Pero generalmente son meros azúcares sintácticos para los functors. Una lambda se traduce directamente en un functor. Todo lo que está dentro []se convierte en parámetros de constructor y miembros del objeto functor, y los parámetros dentro ()se convierten en parámetros para el functor operator().

Una lambda que no captura variables (nada dentro de la []'s) se puede convertir en un puntero de función (MSVC2010 no admite esto, si ese es su compilador, pero esta conversión es parte del estándar).

Pero el tipo real de lambda no es un puntero de función. Es un tipo de functor no especificado.


1
MSVC2010 no admite la conversión al puntero de función, pero MSVC11 sí. blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

18
+1 para "simple azúcar sintáctico para functors". Se puede evitar mucha confusión potencial al recordar esto.
Ben

44
un functor es cualquier cosa con operator()básicamente stackoverflow.com/questions/356950/c-functors-and-their-uses
TankorSmash

107

Es una estructura única sin nombre que sobrecarga el operador de llamada de función. Cada instancia de una lambda introduce un nuevo tipo.

En el caso especial de una lambda que no captura, la estructura además tiene una conversión implícita a un puntero de función.


2
Buena respuesta. Mucho más preciso que el mío. +1 :)
jalf

44
+1 para la parte de unicidad, es muy sorprendente al principio y merece atención.
Matthieu M.

No es que realmente importe, pero ¿el tipo realmente no tiene nombre, o simplemente no se le da un nombre hasta el momento de la compilación? IOW, ¿podría uno usar RTTI para encontrar el nombre que decidió el compilador?
Ben

3
@Ben, no tiene nombre y, en lo que respecta al lenguaje C ++, no existe el "nombre que el compilador decida". El resultado de type_info::name()está definido por la implementación, por lo que puede devolver cualquier cosa. En la práctica, el compilador nombrará el tipo por el bien del enlazador.
avakar

1
Últimamente, cuando se me hace esta pregunta, generalmente digo que el tipo de lambda tiene un nombre, el compilador lo sabe, es simplemente indescriptible.
Andre Kostur

24

[C++11: 5.1.2/3]: El tipo de la expresión lambda (que también es el tipo del objeto de cierre) es un tipo de clase sin unión único, sin nombre, llamado tipo de cierre , cuyas propiedades se describen a continuación. Este tipo de clase no es un agregado (8.5.1). El tipo de cierre se declara en el ámbito de bloque más pequeño, ámbito de clase o ámbito de espacio de nombres que contiene la expresión lambda correspondiente . [..]

La cláusula continúa para enumerar las diferentes propiedades de este tipo. Aquí hay algunos puntos destacados:

[C++11: 5.1.2/5]:El tipo de cierre para un lambda-expresión tiene un público inlineoperador de llamada de función (13.5.4) cuyos parámetros y tipo de retorno se describen por el lambda-expresión ‘s declaración-parámetro-cláusula y de tipo de retorno de salida respectivamente. [..]

[C++11: 5.1.2/6]:El tipo de cierre para un lambda-expresión sin lambda de captura tiene una función pública no virtual no explícita conversión a const puntero a la función que tienen los mismos parámetros y valores de retorno como operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que invocar al operador de llamada de función del tipo de cierre.

La consecuencia de este pasaje final es que, si se ha utilizado una conversión, que sería capaz de asignar LAMBDAa pFptr.


3
#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

Los tipos de funciones son de hecho iguales, pero la lambda introduce un nuevo tipo (como un functor).


Recomiendo el enfoque de inmovilización CXXABI si ya va por esta ruta. En cambio, usualmente uso __PRETTY_FUNCTION__, como en template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }, y me quito el extra si comienza a llenarse. Prefiero ver los pasos que se muestran en la sustitución de plantillas. Si falta __PRETTY_FUNCTION__, existen alternativas para MSVC, etc., pero los resultados siempre dependen del compilador por la misma razón por la que CXXABI es necesario.
John P

1

También debe tener en cuenta que lambda es convertible a puntero de función. Sin embargo, typeid <> devuelve un objeto no trvial que debe diferir de lambda a puntero de función genérico. Entonces, la prueba para typeid <> no es una suposición válida. En general, C ++ 11 no quiere que nos preocupemos por la especificación de tipo, todo lo que importa es si un tipo dado es convertible a un tipo de destino.


Es justo, pero los tipos de impresión contribuyen en gran medida a llegar al tipo correcto, sin mencionar la captura de los casos en que el tipo es convertible pero no satisface otras restricciones. (Siempre presionaría para "reificar" las restricciones siempre que sea posible, pero alguien que intente hacerlo tiene más razones para mostrar su trabajo durante el desarrollo)
John P

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.