Considere el siguiente ejemplo:
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
¿Por qué necesito capturar n
en la segunda lambda pero no m
en la primera lambda? Revisé la sección 5.1.2 ( Expresiones Lambda ) en el estándar C ++ 14 pero no pude encontrar una razón. ¿Puede señalarme un párrafo en el que se explique esto?
Actualización: observé este comportamiento con GCC 6.3.1 y 7 (troncal). Clang 4.0 y 5 (troncal) fallan con un error en ambos casos ( variable 'm' cannot be implicitly captured in a lambda with no capture-default specified
).
c++
lambda
constants
language-lawyer
s3rvac
fuente
fuente
constexpr
vsconst
m;
am + 0;
std::array<int,m>
está bien ystd::array<int,n>
no.constexpr
menos que lo especifiques explícitamente. Si alguien quiere unaconstexpr
variable, debe declararla como una.m
no lo esconstexpr
, es una expresión integral constante en tiempo de compilación ... que era algo mucho antes de queconstexpr
se inventara la palabra clave.Respuestas:
Para una lambda en el alcance del bloque, las variables que cumplen con ciertos criterios en el alcance de alcance pueden usarse de manera limitada dentro de la lambda, incluso si no se capturan.
En términos generales, alcanzar el alcance incluye cualquier variable local a la función que contiene el lambda, que estaría dentro del alcance en el punto en que se definió el lambda. Entonces esto incluye
m
yn
en los ejemplos anteriores.Los "ciertos criterios" y las "formas limitadas" son específicamente (a partir de C ++ 14):
m;
es uno de estos), oconst
, novolatile
entero o enumeración cuyo inicializador era una expresión constante , oconstexpr
, novolatile
variable (o un subobjeto de los mismos)Referencias a C ++ 14: [expr.const] /2.7, [basic.def.odr] / 3 (primera oración), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.
El fundamento de estas reglas, como lo sugieren otros comentarios / respuestas, es que el compilador necesita poder "sintetizar" una lambda sin captura como una función libre independiente del bloque (ya que tales cosas se pueden convertir en un puntero) funcionar); puede hacerlo a pesar de referirse a la variable si sabe que la variable siempre tendrá el mismo valor, o puede repetir el procedimiento para obtener el valor de la variable independientemente del contexto. Pero no puede hacer esto si la variable puede diferir de vez en cuando, o si se necesita la dirección de la variable, por ejemplo.
En su código,
n
fue inicializado por una expresión no constante. Porn
lo tanto , no se puede utilizar en una lambda sin ser capturado.m
se inicializó mediante una expresión constante42
, por lo que cumple con "ciertos criterios". Una expresión de valor descartado no odr-usa la expresión, por lo quem;
puede usarse sinm
ser capturada. gcc es correcto.Yo diría que la diferencia entre los dos compiladores es que clang considera
m;
odr-usem
, pero gcc no. La primera oración de [basic.def.odr] / 3 es bastante complicada:pero al leer con atención, menciona específicamente que una expresión de valor descartado no utiliza odr la expresión.
La versión de C ++ 11 de [basic.def.odr] originalmente no incluía el caso de expresión de valor descartado, por lo que el comportamiento de clang sería correcto bajo el C ++ 11 publicado. Sin embargo, el texto que aparece en C ++ 14 se aceptó como un Defecto contra C ++ 11 ( Problema 712 ), por lo que los compiladores deberían actualizar su comportamiento incluso en el modo C ++ 11.
fuente
m;
conversión de lvalor a rvalor es irrelevante. La definición de odr-use en [basic.def.odr] / 3 dice "o se aplica la conversión lvalue-a-rvaluee
oe
es una expresión de valor descartado", y aquí la expresiónm
es una expresión de valor descartado, así que al final la variablem
no se usa odr.Es porque es una expresión constante, el compilador la trata como si fuera
[] { 42; }();
La regla en [ expr.prim.lambda ] es:
Aquí una cita del estándar [ basic.def.odr ]:
(Se eliminó la parte no tan importante para que sea breve)
Mi comprensión simple es: el compilador sabe que
m
es constante en tiempo de compilación, mientrasn
que cambiará en tiempo de ejecución y, por lo tanto,n
debe capturarse.n
se usaría odr, porque en realidad debe echar un vistazo a lo que hay dentron
en tiempo de ejecución. En otras palabras, el hecho de que "sólo puede haber una" definición den
es relevante.Esto es de un comentario de MM:
Vea aquí una demostración .
fuente
n;
odr-usesn
, perom;
no odr-usem
n
pero nom
.EDITAR: La versión anterior de mi respuesta era incorrecta. El principiante es correcto, aquí hay una cotización estándar relevante:
[basic.def.odr]
Dado que
m
es una expresión constante, no se usa odr y, por lo tanto, no es necesario capturarla.Parece que el comportamiento de los sonidos metálicos no cumple con el estándar.
fuente