Может ли лямбда-выражение, ничего не фиксирующее, получить доступ к глобальным переменным?

c #lambda #scope

#c #c 11 #лямбда #глобальные переменные #стандарты

Вопрос:

 int n;    
int main()
{
    [](){ n = 0; }(); // clang says "ok"

    int m;
    [](){ m = 0; }(); // clang says "not ok"
}
 

Мне просто интересно:

Если лямбда-выражение ничего не фиксирует, разрешено ли ему получать доступ к глобальным переменным в соответствии со стандартом C ?

Комментарии:

1. Я бы предположил, что это так, учитывая, что вы можете использовать другие глобальные вещи (функции и типы), не захватывая их. Представьте, если бы вам пришлось захватывать функции алгоритма C ( std::find например), чтобы использовать их из лямбд.

2. en.cppreference.com/w/cpp/language/lambda говорит о чем — то capture-default таком . Я не мог подробно разобраться, что он делает.

3. Если вы подумаете об этом, лямбда-выражение — это просто короткий путь к определению a struct с помощью оператора функции. Локальные переменные не входят в область struct видимости функций-членов, но глобальные переменные есть.

4. Глобальные переменные не могут быть захвачены.

5. @cpplearner «Глобальные переменные не могут быть захвачены».? Любая ссылка?

Ответ №1:

Да, конечно. Применяются обычные правила поиска имен.

[expr.prim.lambda]/7 … для целей поиска имен… составная инструкция рассматривается в контексте лямбда-выражения.

Re: почему локальные переменные обрабатываются иначе, чем глобальные.

[expr.prim.lambda]/13 … Если лямбда-выражение или создание экземпляра шаблона оператора вызова функции универсального лямбда-odr-uses (3.2) this или переменная с автоматической продолжительностью хранения из области ее достижения, эта сущность должна быть захвачена лямбда-выражением.

[expr.prim.lambda]/9 Лямбда-выражение, наименьшей объемлющей областью которого является область блока (3.3.3), является локальным лямбда-выражением… Область действия локального лямбда-выражения — это набор охватывающих областей вплоть до самой внутренней охватывающей функции и ее параметров включительно.

В вашем примере m это переменная с автоматической продолжительностью хранения из области действия лямбды, и поэтому она должна быть захвачена. n нет, и поэтому не должно быть.

Комментарии:

1. В приведенной цитате ничего не говорится о глобальных переменных.

2. Если n локально, код является незаконным. Почему это законно, если n является глобальным?

3. @xmllmx добавил объяснение того, почему локальные переменные ведут себя по-разному.

Ответ №2:

На самом деле [](){ n = 10; }(); он ничего не фиксирует, вместо этого он использует глобальную переменную.

 int n;    
int main()
{
    [](){ n = 10; }(); // clang says "ok"
    std::cout << n; // output 10
}
 

Смотрите capture-list в пояснении

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

Список захвата можно передать следующим образом (подробное описание см. Ниже):

  • [a,amp; b] где a захватывается копией, а b захватывается ссылкой.
  • [this] фиксирует текущий объект (* this) по ссылке
  • [amp;] фиксирует все автоматические переменные, используемые в теле лямбда-выражения, по ссылке и текущий объект по ссылке, если он существует
  • [=] фиксирует все автоматические переменные, используемые в теле лямбда-выражения, путем копирования и текущего объекта по ссылке, если существует
  • [ ] ничего не фиксирует

Комментарии:

1. » текущий объект по ссылке , если он существует»? Что это? Не могли бы вы объяснить это более подробно?

Ответ №3:

По умолчанию доступны глобальные, статические и постоянные переменные:

 #include <iostream>

int n;    
int main()
{
    [](){ n = 10; }();
    std::cout << n << std::endl;
    static int m = 1;
    [](){ m = 100; }();
    std::cout << m << std::endl;
    const int l = 200;
    [](){ std::cout << l << std::endl; }();
}