Почему лямбда-оператор вызова неявно const? - PullRequest
0 голосов
/ 23 ноября 2018

У меня есть небольшое «лямбда-выражение» в функции ниже:

int main()
{
    int x = 10;
    auto lambda = [=] () { return x + 3; };
}

Ниже приведен «класс анонимного замыкания», сгенерированный для вышеуказанного лямбда-выражения.

int main()
{
    int x = 10;

    class __lambda_3_19
    {
        public: inline /*constexpr */ int operator()() const
        {
            return x + 3;
        }

        private:
            int x;

        public: __lambda_3_19(int _x) : x{_x}
          {}

    };

    __lambda_3_19 lambda = __lambda_3_19{x};
}

«оператор ()» замыкания, сгенерированный компилятором, неявно является константным.Почему стандартный комитет сделал его const по умолчанию?

Ответы [ 3 ]

0 голосов
/ 23 ноября 2018

Нашел эту бумагу Херба Саттера на open-std.org, где обсуждается этот вопрос.

Странная пара: захват по введенному значению const и причудливый изменчивый
Рассмотрим этот пример, где программист захватывает локальную переменную по значению и пытается изменить захваченное значение (которое является переменной-членом лямбда-объекта):

int val = 0;
auto x = [=]( item e ) // look ma, [=] means explicit copy
 { use( e, ++val ); }; // error: count is const, need ‘mutable’
auto y = [val]( item e ) // darnit, I really can’t get more explicit
 { use( e, ++val ); }; // same error: count is const, need ‘mutable’

Эта функция, похоже, была добавлена ​​из-за опасений, что пользователь может не осознавать, что он получил копию, и, в частности, поскольку лямбды можно копировать, он может менять другую копию лямбды.

Приведенная выше цитата и пример указывают, почему Комитет по стандартам мог сделать его const по умолчанию и потребовать mutable для его изменения.

0 голосов
/ 23 ноября 2018

Я думаю, это просто, чтобы избежать путаницы, когда переменная внутри лямбды ссылается не на то, что было первоначально захвачено.Лексически такая переменная как бы находится в области своего «оригинала».Копирование в основном позволяет продлить срок службы объекта.Когда захват не является копией, он ссылается на оригинал, и к оригиналу применяются модификации, и нет никакой путаницы из-за двух разных объектов (один из которых вводится неявно), и это разрешено оператором вызова константной функции лямбды.

0 голосов
/ 23 ноября 2018

С cppreference

Если в лямбда-выражении не было использовано ключевое слово mutable, оператор вызова функции является квалифицированным const, а объекты, которые были захваченыкопия не может быть изменена изнутри этого operator()

В вашем случае нет ничего, что может быть изменено при захвате копии.

Я полагаю, что если вы что-то напишитекак

int x = 10;

auto lambda = [=] () mutable { x += 3; return x; };

const должно исчезнуть

- РЕДАКТИРОВАТЬ -

Точность OP

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

Я не адвокат по языку, но это кажется мне очевидным: если вы сделаете operator() не const, вы не можете сделать что-то как

template <typename F>
void foo (F const & f)
 { f(); }

// ...

foo([]{ std::cout << "lambda!" << std::endl; });

Я имею в виду ... если operator() не const, вы не можете использовать лямбды, передавая их как const ссылку.

А когда это строго не нужно, должно быть недопустимое ограничение.

...