Penso che Clang possa effettivamente essere corretto.
Secondo [lambda.capture] / 11 , un'espressione id usata nella lambda si riferisce al membro acquisito per copia della lambda solo se costituisce un uso dispari . In caso contrario, si riferisce all'entità originale . Questo vale per tutte le versioni di C ++ da C ++ 11.
Secondo [basic.dev.odr] / 3 di C ++ 17, una variabile di riferimento non viene utilizzata in modo dispari se l'applicazione di una conversione da valore in valore produce un'espressione costante.
Nella bozza C ++ 20, tuttavia, il requisito per la conversione da lvalue a rvalue viene eliminato e il passaggio rilevante viene modificato più volte per includere o meno la conversione. Vedi il numero 1472 di CWG e il numero 1741 di CWG , nonché il numero 2083 di CWG aperto .
Poiché m
è inizializzato con un'espressione costante (riferita a un oggetto durata della memoria statica), usandolo si ottiene un'espressione costante per eccezione in [expr.const] /2.11.1 .
Questo non è tuttavia il caso in cui vengono applicate le conversioni da lvalue a rvalue, poiché il valore di n
non è utilizzabile in un'espressione costante.
Pertanto, a seconda che si supponga che le conversioni da lvalue a rvalue debbano essere applicate nel determinare l'uso odr, quando si usa m
in lambda, può o meno riferirsi al membro del lambda.
Se la conversione deve essere applicata, GCC e MSVC sono corretti, altrimenti Clang lo è.
Puoi vedere che Clang cambia comportamento se cambi l'inizializzazione di m
non essere più un'espressione costante:
#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;
}
In questo caso tutti i compilatori concordano sul fatto che l'output è
100 223 100
perché m
in lambda farà riferimento al membro della chiusura di tipo int
inizializzato dalla variabile di riferimento m
in f
.