Myślę, że Clang może mieć rację.
Według [lambda.capture] / 11 , wyrażenie id użyte w lambda odnosi się do elementu przechwyconego przez lambda po kopii, tylko jeśli stanowi użycie nieprzydatne . Jeśli nie, oznacza to pierwotny byt . Dotyczy to wszystkich wersji C ++ od C ++ 11.
Według [ ++. dev.odr] / 3 C ++ 17 zmienna referencyjna nie jest używana odr, jeśli zastosowanie do niej konwersji wartości z wartości na wartość daje stałe wyrażenie.
W wersji roboczej C ++ 20 jednak wymóg dotyczący konwersji wartości z wartości na wartość jest odrzucany, a odpowiedni fragment wielokrotnie zmieniany, aby uwzględnić konwersję lub nie. Patrz wydanie CWG 1472 i wydanie CWG 1741 , a także wydanie otwarte CWG 2083 .
Ponieważ mjest inicjowany stałym wyrażeniem (odnoszącym się do obiektu o czasie trwania przechowywania statycznego), użycie go daje stałe wyrażenie na wyjątek w [expr.const] /2.11.1 .
Nie dzieje się tak jednak w przypadku zastosowania konwersji wartości z wartości na wartość, ponieważ wartość parametru nnie jest użyta w wyrażeniu stałym.
W związku z tym, w zależności od tego, czy konwersje wartości z wartości na wartość mają być stosowane przy określaniu użycia odry, podczas korzystania mz lambda może, ale nie musi odnosić się do członka lambda.
Jeśli konwersja powinna być zastosowana, GCC i MSVC są poprawne, w przeciwnym razie Clang jest.
Możesz zobaczyć, że Clang zmienia to zachowanie, jeśli zmienisz inicjalizację, maby nie była już stałym wyrażeniem:
#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;
}
W tym przypadku wszystkie kompilatory zgadzają się, że dane wyjściowe są
100 223 100
ponieważ mw lambda będzie odnosić się do elementu zamknięcia, który jest typu intinicjalizowany kopiowaniem ze zmiennej odniesienia mw f.