GCC неправильно отображает глобальные переменные по ссылке в лямбда-функциях?
Кажется, что GCC неправильно отображает глобальные переменные по ссылке в лямбда-функциях, даже если они указаны как "захват по значению". Этот код будет компилировать и печатать "a = 9":
#include <iostream>
int a = 10;
int main()
{
[=]() { a = 9; } ();
std::cout << "a = " << a << std::endl;
return 0;
}
Пока этот код не будет компилироваться:
#include <iostream>
int main()
{
int a = 10;
[=]() { a = 9; } (); // error: assignment of member 'main()::<lambda()>::a' in read-only object
std::cout << "a = " << a << std::endl;
return 0;
}
Но явно фиксируя глобальное значение по значению, а затем присваивая ему, появляется ошибка:
#include <iostream>
int a = 10;
int main()
{
[a]() { a = 9; } (); // assigment of read-only object
std::cout << "a = " << a << std::endl;
return 0;
}
Я уверен, что ошибка - правильное поведение - почему неявный захват обходит эту ошибку? Я просто изучаю новые возможности С++ 11 и случайно написал первый фрагмент кода (не понимая, что это должна быть ошибка), и затем был удивлен, когда изменения в том, что я предположил, были локальной переменной, повлияли на глобальную.
Так как ошибка должна быть назначена переменной захваченного значения в лямбда, GCC предположительно использует ссылку на переменную для целей оптимизации, по крайней мере в этом случае, и не обнаруживает ошибочное назначение.
Ответы
Ответ 1
§5.1.2/11:
Если * lambda-expression (имеет связанный с захватом-умолчанию и его составной оператор odr-использует (3.2) this
или переменную с продолжительностью автоматического хранения и объект, использующий odr явно не захвачен, то объект, использующий odr, считается неявным захватом,...
Глобальные переменные имеют статическую продолжительность хранения (§3.7.1), поэтому глобальный a
не будет неявно зафиксирован по значению. Тем не менее, вы можете получить доступ к глобальной переменной где угодно, поэтому
[=]() { a = 9; } ();
установит глобальный a
в 9, как ожидалось.
Явно захватить глобальную должна быть ошибка или UB, потому что в §5.1.2/10 говорится
Идентификаторы в списке захвата просматриваются с использованием обычных правил для поиска неквалифицированных имен (3.4.1); каждый такой поиск найдет переменную с автоматической продолжительностью хранения, объявленной в области охвата локального выражения лямбда.