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
y n
en los ejemplos anteriores.
Los "ciertos criterios" y las "formas limitadas" son específicamente (a partir de C ++ 14):
- Dentro de la lambda, la variable no debe ser usada odr , lo que significa que no debe sufrir ninguna operación excepto por:
- que aparece como una expresión de valor descartado (
m;
es uno de estos), o
- recuperar su valor.
- La variable debe ser:
- A
const
, no volatile
entero o enumeración cuyo inicializador era una expresión constante , o
- A
constexpr
, no volatile
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. Por n
lo tanto , no se puede utilizar en una lambda sin ser capturado.
m
se inicializó mediante una expresión constante 42
, por lo que cumple con "ciertos criterios". Una expresión de valor descartado no odr-usa la expresión, por lo que m;
puede usarse sin m
ser capturada. gcc es correcto.
Yo diría que la diferencia entre los dos compiladores es que clang considera m;
odr-use m
, pero gcc no. La primera oración de [basic.def.odr] / 3 es bastante complicada:
Una variable x
cuyo nombre aparece como una expresión potencialmente evaluada ex
es utilizada por odr aex
menos que la aplicación de la conversión lvalue-a-rvalue x
produzca una expresión constante que no invoca ninguna función no trivial y, si x
es un objeto, ex
es un elemento de el conjunto de resultados potenciales de una expresión e
, donde se aplica la conversión de lvalue a rvalue e
o e
es una expresión de valor descartado.
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.
constexpr
vsconst