Рассмотрим следующий пример:
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
Зачем мне нужно, чтобы захватить N
в Вторая лямда, но не " м " в первой лямбды? Я проверил раздел 5.1.2 (Lambda expressions) в стандарте C++14, но я не смог найти причину. Можете ли вы указать мне на пункт, в котором это описано?
Обновление: я заметил такое поведение с ССЗ 6.3.1 и 7 (багажник). Лязг 4.0 и 5 (багажник) выдает сообщение об ошибке в обоих случаях (переменная 'м' не может быть, косвенно лямбда без захвата-по умолчанию
).
Для лямбда на блочную область видимости, переменные, отвечающие определенным критериям в охват может быть использован несколькими способами, внутри лямбда-выражения, даже если они не захватили.
Грубо говоря, охват включает в себя любая переменная локальная для функции, содержащей лямбда, что бы быть в контексте в точке лямбда была определена. Так что это включает в себя M
и N
в приведенных выше примерах.
В "определенных критериев" и "можно" и конкретно (как в C++14):
м;
- одна из них), иликак const
, нелетучих
целое число или enum инициализатором которого был константное выражение, илитаких
, нелетучих
переменной (или суб-объект такой)Ссылки на C++14: [выраж.константный]/2.7, [основной.деф.Усо]/3 (первое предложение), [выраж.прим.лямбда]/12, [выраж.прим.лямбда]/10.
Обоснование этих правил, как полагают другие комментарии/ответы, заключается в том, что компилятор должен быть в состоянии, чтобы "Синтез" А нет-захват лямбда как функции независимых блока (так как такие вещи может быть преобразован в указатель на функцию); он может сделать это, несмотря на отсыл к переменной, если она знает, что переменная всегда будет иметь то же значение, или он может повторить процедуру для получения переменной'самостоятельное значение в контексте. Но это может'т сделать это, если переменная может отличаться от времени, или если переменная'ы адрес нужен, например.
В ваш код, н
был инициализирован не константное выражение. Поэтому " н " не может быть использован в лямбда без захвата.
"м" была инициализирована константным выражением 42
, поэтому он отвечает "по определенным критериям и". Отбрасываются-значение выражения не Усо-использовать выражения, так что М;
может быть использована без " м " попасть в плен. ССЗ является правильным.
Я бы сказал, что разница между двумя компиляторами-это то, что Clang считает, что М;
для Усо-использовать "м", но НКУ не. Первое предложение [основная.деф.Усо]/3-это довольно сложная:
По переменной "х", чье имя появляется в качестве потенциально-вычисляемое выражение
экс
является Усо-используется побывшие
если применением значения-для-правосторонним значением преобразования кХ
дает константное выражение, которое не вызывает каких-либо нетривиальных функций и, еслиX
- это объект,экс
является элементом множества потенциальных результатов выражения "е", где именующее-на-rvalue преобразования применяется ке
илиЕ
- это отбрасывается,-значение выражения.
но прочитав внимательно это делает специально отметить, что отбрасываются-значение выражения не Усо-использовать выражение.
В C++11's версия [основная.деф.Усо] изначально не включать отброшены значение выражения Case, так что дзынь'ы поведение будет правильным в соответствии с опубликовано в C++11. Однако текст, который появляется в C++14 был принят в качестве дефекта против C++11 (выпуск 712), так что компиляторы должны обновлять свое поведение даже в режиме c++11.
Потому что это постоянное выражение, компилятор воспринимает это как если бы он был[] { 42; }();
Правила в [выраж.прим.лямбда] это:
если лямбда-выражение или экземпляра вызова функции шаблон оператора универсального лямбда Усо-используется (3.2) это или переменная с автоматическая длительность хранения из ее охват, этот субъект должен быть захвачен лямбда-выражение.
Вот цитата из стандарта [основная.деф.Усо]:
По переменной x, чье имя появляется в качестве потенциально-вычисляемое выражение Ex-это Усо-используется, если применение именующее-на-rvalue преобразования для Х дает константное выражение (...) или Е-это отбрасывается,-значение выражения.
(Снято не очень важная часть, чтобы держать его коротким)
Мое простое объяснение: компилятор знает, что " М "является константой во время компиляции, в то время как N
будет меняться во время выполнения, и поэтому" н "должен быть пойман. н
будет Усо-используется, потому что вы должны реально взглянуть на то, что находится внутри" N " на время выполнения. Другими словами, тот факт, что "может быть только одно" но определение N
является актуальным.
Это из комментария М. М.:
M-это константное выражение, потому что он'ы константный автоматической переменной с постоянным инициализатора выражение, но n не является постоянной выражение инициализатор, потому что его не было константным выражением. Это транслируется в [выраж.константный]/2.7. Константное выражение-это не УСО-используется, по первому предложению [основная.деф.Усо]/3
Смотри здесь демо.
Редактировать: предыдущий вариант моего ответа был неправ. Новичок's-это правильно, вот это соответствующего стандарта цитата:
- Переменной х, чье имя появляется в качестве потенциально-вычисляемое выражение Ex является Усо-используется экс без применения [именующее-на-правосторонним значением](http://eel.is/c++проект/усл.преобразование lval) по X дает константное выражение, что не вызывает каких-либо нетривиальных функций и, если X-это объект, экс-элемент из набора возможных результатов выражением E, где значения-для-правосторонним значением преобразования применяется к е или Е-это отбрасывается,-значение выражения. ...
С м
- это константное выражение, то это не Усо-используется, и, следовательно, не должны быть захвачены.
Похоже, что звон поведение не соответствует стандарту.