Для лямбди в області блоку - змінні, що відповідають певним критеріям в області охоплення можуть використовуватися обмежено в ламбда-середовищі, навіть якщо вони не захоплені.
Грубо кажучи, охоплення сфери охоплення включає будь-яку змінну, локальну для функції, що містить лямбда-випромінювання, яка була б у зоні дії в момент визначення лямбда-сигналу. Отже, це включає 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