Creo que Clang en realidad puede ser correcto.
De acuerdo con [lambda.capture] / 11 , una expresión id utilizada en el lambda se refiere al miembro de lambda capturado por copia solo si constituye un uso odr . Si no es así, se refiere a la entidad original . Esto se aplica a todas las versiones de C ++ desde C ++ 11.
De acuerdo con [++.dev.odr] / 3 de C ++ 17, una variable de referencia no se utiliza odr si la conversión de valor-valor-valor produce una expresión constante.
Sin embargo, en el borrador de C ++ 20, el requisito para la conversión de valor a valor se cae y el pasaje relevante cambió varias veces para incluir o no la conversión. Consulte el número 1472 de CWG y el número 1741 de CWG , así como el número 2083 de CWG abierto .
Como m
se inicializa con una expresión constante (que se refiere a un objeto de duración de almacenamiento estático), su uso produce una expresión constante por excepción en [expr.const] /2.11.1 .
Sin embargo, este no es el caso si se aplican las conversiones de valor a valor, porque el valor de n
no es utilizable en una expresión constante.
Por lo tanto, dependiendo de si se supone que las conversiones lvalue-to-rvalue se deben aplicar para determinar el uso de odr, cuando se usa m
en el lambda, puede referirse o no al miembro del lambda.
Si se debe aplicar la conversión, GCC y MSVC son correctos, de lo contrario, Clang lo es.
Puede ver que Clang cambia su comportamiento si cambia la inicialización de m
para que ya no sea una expresión constante:
#include <stdio.h>
#include <functional>
int n = 100;
void g() {}
std::function<int()> f()
{
int &m = (g(), n);
return [m] () mutable -> int {
m += 123;
return m;
};
}
int main()
{
int x = n;
int y = f()();
int z = n;
printf("%d %d %d\n", x, y, z);
return 0;
}
En este caso, todos los compiladores están de acuerdo en que la salida es
100 223 100
porque m
en lambda se referirá al miembro del cierre que es de tipo int
copy-initialized de la variable de referencia m
en f
.