Я думаю, что на самом деле Clang может быть правильным.
Согласно [lambda.capture] / 11 , id-выражение , используемое в лямбде, относится к Член лямбды, захваченный копией, только если он составляет odr-use . Если это не так, то это относится к исходной сущности . Это относится ко всем версиям C ++ начиная с C ++ 11.
Согласно C ++ 17 [basi c .dev.odr] / 3 ссылочная переменная не используется odr если применение преобразования lvalue в rvalue к нему приводит к постоянному выражению.
Однако в черновике C ++ 20 требование преобразования lvalue в rvalue отбрасывается, и соответствующий фрагмент изменялся несколько раз, чтобы включить или не включать преобразование. См. выпуск CWG 1472 и выпуск CWG 1741 , а также открытый выпуск CWG 2083 .
Поскольку m
инициализируется с константой Выражение (ссылается на объект длительности хранения stati c), используя его, выдает постоянное выражение для исключения в [expr.const] /2.11.1.
Это не так однако, если применяются преобразования lvalue-в-значение, потому что значение n
не может использоваться в константном выражении.
Следовательно, в зависимости от того, предполагается ли применять преобразования lvalue-в-значение при определении использования odr, когда вы используете m
в лямбде, оно может относиться или не относиться к члену лямбды.
Если должно применяться преобразование, G CC и MSV C верны, в противном случае Clang имеет значение.
Вы можете видеть, что Clang меняет свое поведение, если вы измените инициализацию m
, чтобы она больше не была постоянным выражением:
#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;
}
В этом В случае, если все компиляторы согласны с тем, что вывод равно
100 223 100
, поскольку m
в лямбде будет ссылаться на элемент замыкания, который имеет тип int
, инициализированный копией из ссылочной переменной m
в f
.