Подтвердить что ты не робот

Почему const int (или шорты) фиксируются неявно в лямбдах?

Это компилируется:

int main() {
    const int x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

Но это:

int main(){
    const float x = 123;
    auto g = []() { std::cout << x << "\n"; };
    g();
}

дает:

":" x "не записывается"

Почему?

Я тестировал его как на GCC (различные версии от 5.0.0 до 8.0.0), так и на Clang (различные версии от 4.0.0 до 6.0.0). Он ведет себя одинаково во всех случаях.

4b9b3361

Ответ 1

Lambda scope может неявно захватывать переменные в пределах своей области охвата.

Ваши переменные находятся в области охвата, поскольку они локальны для (основной) функции, которая определяет лямбда.

Однако существуют определенные критерии, в которых переменные могут быть захвачены с помощью этого механизма, как указано в [expr.prim.lambda]/12

Лямбда-выражение с ассоциированным захватом-умолчанию, которое не явно фиксировать эту или переменную с автоматической продолжительностью хранения [..], говорится неявно захватить объект (т.е. эту или переменную ), если соединение-оператор:

-odr-uses ([basic.def.odr]) объект или

-значает объект в потенциально-оцененном выражении ([basic.def.odr]), где охватывающее полное выражение зависит от общий параметр лямбда, объявленный в пределах охвата лямбда-выражение.

Самая важная часть - [expr.const]/2.7:

условное выражение e является выражением константы основной константы, если только оценка e, [..] будет оценивать одно из следующих выражений:

преобразование lvalue-to-rvalue ([conv.lval]), если оно не применяется к:

нестабильное значение gl интегрального или перечисления типа, которое относится к энергонезависимому объекту const с предшествующей инициализацией, инициализируется постоянным выражением.

So const int - это основное выражение константы, а const float - нет.

Более того [expr.const] 1826 упоминает:

Сопряженное целое число, инициализированное константой, может использоваться в константных выражениях, но переменная с плавающей запятой const, инициализированная константой, не может.

Подробнее в Почему переменная const иногда не требуется записывать в лямбда?

Ответ 2

С++ 14 draft N4140 5.1.2.12 [expr.prim.lambda]:

Лямбда-выражение с ассоциированным захватом-умолчанию, которое не явно фиксировать эту или переменную с автоматической продолжительностью хранения (это исключает любое идентификационное выражение, которое, как было установлено, ссылается на init-capture, связанный с нестатическим элементом данных), называется неявно захватывать объект (т.е. эту или переменную), если соединение-оператор:

odr использует (3.2) объект или

называет объект в потенциально-оцененном выражении (3.2), где включение полного выражения зависит от общего параметра лямбда объявленный в пределах охвата лямбда-выражения.

Кроме того, .open-std.org:

Сопряженное целое число с константой может использоваться в константах выражения, но переменная с плавающей запятой const, инициализированная константа не может. Это было намеренно, чтобы быть совместимым с С++ 03 одновременно поощряя последовательное использование constexpr. Некоторые люди Однако это различие было удивительным.

Наблюдалось также, что допускающие переменные с постоянной плавающей запятой как постоянные выражения будут изменением ABI, поскольку оно влияют на захват лямбда.