Для лямбди в області блоку - змінні, що відповідають певним критеріям в області охоплення можуть використовуватися обмежено в ламбда-середовищі, навіть якщо вони не захоплені.
Грубо кажучи, охоплення сфери охоплення включає будь-яку змінну, локальну для функції, що містить лямбда-випромінювання, яка була б у зоні дії в момент визначення лямбда-сигналу. Отже, це включає mі nнаведені вище приклади.
"Певні критерії" та "обмежені шляхи" конкретно (станом на C ++ 14):
- Всередині лямбда змінна не повинна використовуватися з підтримкою , що означає, що вона не повинна піддаватися будь-якій операції, крім:
- відображається як вираз із відкинутими значеннями (
m;є одним із них), або
- з отриманням його значення.
- Змінна повинна бути:
- Число
const, яке не є volatileцілим чи числом, чий ініціалізатор був константним виразом , або
- A
constexpr, не volatileзмінний (або його суб-об’єкт)
Посилання на C ++ 14: [expr.const] /2.7, [basic.def.odr] / 3 (перше речення), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.
Обгрунтування цих правил, як пропонують інші коментарі / відповіді, полягає в тому, що компілятор повинен мати можливість "синтезувати" лямбду без захоплення як вільну функцію, незалежну від блоку (оскільки такі речі можуть бути перетворені на покажчик). функціонувати); він може це зробити, незважаючи на посилання на змінну, якщо знає, що змінна завжди матиме одне і те ж значення, або може повторити процедуру отримання значення змінної незалежно від контексту. Але він не може цього зробити, якщо змінна час від часу може відрізнятися, або якщо адреса змінної потрібна, наприклад.
У вашому коді nбуло ініціалізовано несталий вираз. Тому nне можна використовувати в лямбді без захоплення.
mбув ініціалізований константним виразом 42, тому він відповідає "певним критеріям". Вираз із відкинутими значеннями не використовує вираз, тому m;його можна використовувати без mзахоплення. gcc є правильним.
Я б сказав, що різниця між двома компіляторами полягає в тому, що m;clang вважає odr-use m, а gcc - ні. Перше речення [basic.def.odr] / 3 досить складне:
Змінна x, ім'я якої відображається як потенційно обчислюваний вираз ex, підтримується , exякщо не застосовувати перетворення lvalue-to-rvalue для xотримання постійного виразу, який не викликає ніяких нетривіальних функцій і, якщо xє об'єктом, exє елементом набір потенційних результатів виразу e, де або застосовується перетворення lvalue-to-rvalue e, або eє виразом відкинутих значень.
але при уважному прочитанні він конкретно згадує, що вираз з відкинутими значеннями не використовує вираз.
Версія [basic.def.odr] на C ++ 11 спочатку не включала випадки виразу відкинутих значень, тому поведінка clang була б правильною за опублікованою C ++ 11. Однак текст, що з'являється в C ++ 14, був прийнятий як дефект проти C ++ 11 ( випуск 712 ), тому компілятори повинні оновити свою поведінку навіть у режимі C ++ 11.
constexprпротиconst