compile c++ c++11 lambda language-lawyer shadowing

c++ - compile - Captura de Lambda y parámetro con el mismo nombre: ¿quién sombrea al otro?(clang vs gcc)



clang compile (2)

Actualización: como lo prometió el presidente de Core en la cita inferior, el código ahora está mal formado :

Si un identificador en una captura simple aparece como el ID del declarador de un parámetro de la cláusula-declaración-parámetro del declarador lambda , el programa está mal formado.

Hubo algunos problemas relacionados con la búsqueda de nombres en lambdas hace un tiempo. Fueron resueltos por N2927 :

La nueva redacción ya no se basa en la búsqueda para reasignar los usos de las entidades capturadas. Niega más claramente las interpretaciones de que la declaración compuesta de una lambda se procesa en dos pasadas o que cualquier nombre en esa declaración compuesta podría resolver a un miembro del tipo de cierre.

La búsqueda siempre se realiza en el contexto de la expresión lambda , nunca "después" de la transformación al cuerpo de la función miembro de un tipo de cierre. Ver [expr.prim.lambda]/8 :

La declaración compuesta de la expresión lambda produce el cuerpo de la función ([dcl.fct.def]) del operador de llamada de función, pero para fines de búsqueda de nombres, [...], la declaración compuesta se considera en el contexto de La expresión lambda . [ Ejemplo :

struct S1 { int x, y; int operator()(int); void f() { [=]()->int { return operator()(this->x+y); // equivalent to: S1::operator()(this->x+(*this).y) // and this has type S1* }; } };

- ejemplo final ]

(El ejemplo también deja en claro que la búsqueda de alguna manera no considera el miembro de captura generado del tipo de cierre).

El nombre foo no se (re) declara en la captura; se declara en el bloque que encierra la expresión lambda. El parámetro foo se declara en un bloque que está anidado en ese bloque externo (consulte [basic.scope.block]/2 , que también menciona explícitamente los parámetros lambda). El orden de búsqueda es claramente de bloques internos a externos . Por lo tanto, se debe seleccionar el parámetro, es decir, Clang es correcto.

Si hiciera la captura una captura inicial, es decir, foo = "" lugar de foo , la respuesta no sería clara. Esto se debe a que la captura ahora induce una declaración cuyo "bloque" no se da. Le envié un mensaje al presidente central sobre esto, quien respondió

Este es el número 2211 (en breve aparecerá una nueva lista de temas en el sitio open-std.org, desafortunadamente con solo marcadores de posición para varios temas, de los cuales este es uno; estoy trabajando duro para llenar esos vacíos antes de Kona reunión a fin de mes). CWG discutió esto durante nuestra teleconferencia de enero, y la dirección es hacer que el programa esté mal formado si un nombre de captura también es un nombre de parámetro.

auto foo = "You''re using g++!"; auto compiler_detector = [foo](auto foo) { std::puts(foo); }; compiler_detector("You''re using clang++!");

  • clang ++ 3.6.0 y versiones posteriores imprimen "¡Estás usando clang ++!" y advertir sobre la captura de foo sin usar.

  • g ++ 4.9.0 y versiones posteriores imprimen "¡Estás usando g ++!" y advertir sobre el parámetro foo no utilizado.

¿Qué compilador sigue con mayor precisión el estándar C ++ aquí?

ejemplo de wandbox


Estoy tratando de reunir algunos comentarios a la pregunta para darle una respuesta significativa.
En primer lugar, tenga en cuenta que:

  • Los miembros de datos no estáticos se declaran para la lambda para cada variable capturada por copia
  • En el caso específico, la lambda tiene un tipo de cierre que tiene un operador público de llamada de función de plantilla en línea que acepta un parámetro llamado foo

Por lo tanto, la lógica me haría decir a primera vista que el parámetro debería sombrear la variable capturada como si estuviera en:

struct Lambda { template<typename T> void operator()(T foo) const { /* ... */ } private: decltype(outer_foo) foo{outer_foo}; };

De todos modos, @nm notó correctamente que los miembros de datos no estáticos declarados para variables capturadas con copia en realidad no tienen nombre. Dicho esto, todavía se accede al miembro de datos sin nombre mediante un identificador (que es foo ). Por lo tanto, el nombre del parámetro del operador de llamada de función aún debería (déjame decir) sombrear ese identificador .
Como señaló correctamente @nm en los comentarios a la pregunta:

la entidad original capturada [...] debería sombrearse normalmente de acuerdo con las reglas de alcance

Por eso, diría que el sonido metálico es correcto.