Eu acho que Clang pode estar correto.
De acordo com [lambda.capture] / 11 , uma expressão id usada no lambda refere-se ao membro capturado por cópia do lambda somente se ele constitui um uso de odr . Caso contrário, refere-se à entidade original . Isso se aplica a todas as versões do C ++ desde o C ++ 11.
De acordo com [basic.dev.odr] / 3 do C ++ 17/3, uma variável de referência não é usada odr se a aplicação de conversão lvalue em rvalue produz uma expressão constante.
No rascunho do C ++ 20, no entanto, o requisito para a conversão lvalue em rvalue é descartado e a passagem relevante é alterada várias vezes para incluir ou não a conversão. Consulte a edição 1472 do CWG e a edição 1741 do CWG , bem como a edição 2083 do CWG aberta .
Como mé inicializado com uma expressão constante (referindo-se a um objeto de duração de armazenamento estático), usá-lo gera uma expressão constante por exceção em [expr.const] /2.11.1 .
Esse não é o caso, no entanto, se as conversões lvalue para rvalue forem aplicadas, porque o valor de nnão é utilizável em uma expressão constante.
Portanto, dependendo se as conversões lvalue para rvalue devem ou não ser aplicadas na determinação do uso de odr, quando você usa mo lambda, ele pode ou não se referir ao membro do lambda.
Se a conversão for aplicada, o GCC e o MSVC estão corretos, caso contrário, Clang está.
Você pode ver que o Clang altera seu comportamento se você alterar a inicialização de mpara não ser mais uma expressão 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;
}
Nesse caso, todos os compiladores concordam que a saída é
100 223 100
porque mno lambda irá referir-se ao membro do fecho, que é do tipo de intcópia-inicializada a partir da variável de referência mem f.