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í?
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.