Вложенная лямбда и изменяемое ключевое слово - PullRequest
2 голосов
/ 20 октября 2019

Рассмотрим следующий фрагмент кода:

void f() {
    int a = 3;
    [=]() {
        [=] () mutable {
            a = 5;
        }();
    }();
}

Компилируется на Clang (https://godbolt.org/z/IEXotM), но не на GCC (https://godbolt.org/z/xWXFe6). Ошибка для GCC:

<source>: In lambda function:

<source>:5:15: error: assignment of read-only variable 'a'

    5 |             a = 5;

      |             ~~^~~

Compiler returned: 1

Согласно https://en.cppreference.com/w/cpp/language/lambda,

Необязательная последовательность спецификаторов. Разрешены следующие спецификаторы:

mutable : позволяет телу изменять захваченные параметрыкопировать и вызывать их неконстантные функции-члены

И похоже, что поведение Clang здесь правильно. Это так?

Ответы [ 2 ]

1 голос
/ 20 октября 2019

Если вы читаете из нижней части ссылки, которую вы цитировали, вы можете прочитать:

Если вложенная лямбда-м2 захватывает что-то, что также захватывается непосредственно вмещающей лямбдой m1, тогда захват m2преобразуется следующим образом:

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

    #include <iostream>
    
    int main()
    {
    int a = 1, b = 1, c = 1;
    
    auto m1 = [a, &b, &c]() mutable {
        auto m2 = [a, b, &c]() mutable {
            std::cout << a << b << c << '\n';
            a = 4; b = 4; c = 4;
        };
        a = 3; b = 3; c = 3;
        m2();
    };
    
    a = 2; b = 2; c = 2;
    
    m1();                             // calls m2() and prints 123
    std::cout << a << b << c << '\n'; // prints 234
    }
    

Итак, вы захватываете значение излямбда-оболочка и для ее изменения необходимо сделать ее изменчивой и в лямбду-оболочку.

0 голосов
/ 20 октября 2019

Указание mutable для лямбды верхнего уровня исправляет сборку GCC: https://godbolt.org/z/qGGBgs

Похоже, что в GCC "захватывает" поток через лямбды. т.е. во вложенных лямбда-захватах можно захватывать только то, что захвачено в вышеупомянутой лямбде. Так что, если a не зафиксировано изменчивым в лямбде верхнего уровня, вложенная лямбда не может сделать его изменяемым внезапно.

Я не знаю, приемлемо ли это в соответствии с C ++хотя спец.

...