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ż m
jest 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 n
nie 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 m
z 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ę, m
aby 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ż m
w lambda będzie odnosić się do elementu zamknięcia, który jest typu int
inicjalizowany kopiowaniem ze zmiennej odniesienia m
w f
.