una funcion ejemplo dev define declara constantes constante como calificador c++ lambda const language-lawyer

c++ - funcion - ¿Por qué a veces no es necesario capturar una variable constante con una lambda?



constantes en c++ (3)

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. ¿Me puede señalar un párrafo en el que se explica esto?

Actualización: Observé este comportamiento con GCC 6.3.1 y 7 (troncal). Clang 4.0 y 5 (troncal) falla con un error en ambos casos (la variable ''m'' cannot be implicitly captured in a lambda with no capture-default specified ).


EDITAR: La versión anterior de mi respuesta fue incorrecta. Principiante es correcto, aquí hay una cita estándar relevante:

basic.def.odr

  1. Una variable x cuyo nombre aparece como una expresión potencialmente evaluada ex es odr-utilizada por ex a menos que la aplicación de la conversión de lvalue-to-rvalue a x produzca una expresión constante que no invoque ninguna función no trivial y, si x es un objeto , ex es un elemento del conjunto de resultados potenciales de una expresión e, donde la conversión de valor a valor r se aplica a e, oe es una expresión de valor descartado. ...

Como m es una expresión constante, no se usa odr y, por lo tanto, no necesita ser capturada.

Parece que el comportamiento de clangs no cumple con el estándar.


Es porque es una expresión constante, el compilador trata es como si fuera [] { 42; }(); [] { 42; }();

La regla en [ expr.prim.lambda ] es:

Si una expresión lambda o una instanciación de la plantilla de operador de llamada de función de un lambda genérico odr-utiliza (3.2) esto o una variable con duración de almacenamiento automática desde su alcance de alcance, esa entidad será capturada por la expresión lambda.

Aquí una cita del estándar [ basic.def.odr ]:

Una variable x cuyo nombre aparece como una expresión potencialmente evaluada, ex, se usa odr, a menos que la aplicación de la conversión lvalue-to-rvalue a x produzca una expresión constante (...) oe es una expresión de valor descartado.

(Se eliminó la parte no tan importante para mantenerlo corto)

Mi simple comprensión es: el compilador sabe que m es constante en tiempo de compilación, mientras que n cambiará en tiempo de ejecución y por lo tanto n tiene que ser capturado. n usaría odr, porque tienes que echar un vistazo a lo que hay dentro de n en tiempo de ejecución. En otras palabras, el hecho de que "solo puede haber una" definición de n es relevante.

Esto es de un comentario de MM:

m es una expresión constante porque es una variable automática constante con inicializador de expresión constante, pero n no es una expresión constante porque su inicializador no era una expresión constante. Esto está cubierto en [expr.const] /2.7. La expresión constante no se utiliza ODR, de acuerdo con la primera oración de [basic.def.odr] / 3

Vea aquí para una demo .


Para una lambda en el alcance de bloque, las variables que cumplen 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 en el alcance en el punto en que se definió el lambda. Entonces esto incluye 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 usarse odr , lo que significa que no debe someterse a ninguna operación, excepto:
    • que aparece como una expresión de valor descartado ( m; es uno de estos), o
    • teniendo su valor recuperado.
  • La variable debe ser:
    • Un const , entero no volatile o enumeración cuyo inicializador era una expresión constante , o
    • Una constexpr , no volatile (o un sub-objeto de tal)

Referencias a C ++ 14: [expr.const] /2.7, [basic.def.odr] / 3 (primera oración), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.

La justificación 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 estas cosas pueden convertirse en un puntero) funcionar); puede hacer esto 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 la dirección de la variable es necesaria, por ejemplo.

En su código, n fue inicializado por una expresión no constante. Por lo tanto, n no puede usarse en una lambda sin ser capturado.

m fue inicializado por una expresión constante 42 , por lo que cumple con los "ciertos criterios". Una expresión de valor descartado no utiliza o utiliza la expresión, entonces m; se puede usar sin que m sea ​​capturado. gcc es correcto

Yo diría que la diferencia entre los dos compiladores es que clang considera m; para usar odr 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 odr-utilizada por ex menos que la aplicación de la conversión de lvalue-to-rvalue a x produzca una expresión constante que no invoque ninguna función no trivial y, si x es un objeto , ex es un elemento del conjunto de resultados potenciales de una expresión e , donde la conversión de valor a valor r se aplica a e , e es una expresión de valor descartado.

pero al leer detenidamente, menciona específicamente que una expresión de valor descartado no utiliza o utiliza 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 según el C ++ 11 publicado. Sin embargo, el texto que aparece en C ++ 14 fue aceptado como un defecto contra C ++ 11 ( Problema 712 ), por lo que los compiladores deberían actualizar su comportamiento incluso en modo C ++ 11.