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 nen la segunda lambda pero no men 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

constexprvsconstm;am + 0;std::array<int,m>está bien ystd::array<int,n>no.constexprmenos que lo especifiques explícitamente. Si alguien quiere unaconstexprvariable, debe declararla como una.mno lo esconstexpr, es una expresión integral constante en tiempo de compilación ... que era algo mucho antes de queconstexprse 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
mynen los ejemplos anteriores.Los "ciertos criterios" y las "formas limitadas" son específicamente (a partir de C ++ 14):
m;es uno de estos), oconst, novolatileentero o enumeración cuyo inicializador era una expresión constante , oconstexpr, novolatilevariable (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,
nfue inicializado por una expresión no constante. Pornlo tanto , no se puede utilizar en una lambda sin ser capturado.mse 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 sinmser 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-rvalueeoees una expresión de valor descartado", y aquí la expresiónmes una expresión de valor descartado, así que al final la variablemno 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
mes constante en tiempo de compilación, mientrasnque cambiará en tiempo de ejecución y, por lo tanto,ndebe capturarse.nse usaría odr, porque en realidad debe echar un vistazo a lo que hay dentronen tiempo de ejecución. En otras palabras, el hecho de que "sólo puede haber una" definición denes relevante.Esto es de un comentario de MM:
Vea aquí una demostración .
fuente
n;odr-usesn, perom;no odr-usemnpero 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
mes 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