Почему лямбда-оператор вызова неявно const?
У меня есть небольшое "лямбда-выражение" в функции ниже:
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};
}
Замыкание "operator()", сгенерированное компилятором, неявно const. Почему стандартная комиссия делает его const
по умолчанию?
Ответы
Ответ 1
Из cppreference
Если ключевое слово mutable
не использовалось в лямбда-выражении, оператор вызова функции является константно квалифицированным, а объекты, которые были захвачены копией, не модифицируются внутри этого operator()
В вашем случае нет ничего, что может быть изменено, захваченное копией.
Я полагаю, что если вы напишите что-то
int x = 10;
auto lambda = [=] () mutable { x += 3; return x; };
const
должен исчезнуть
-- РЕДАКТИРОВАТЬ --
ОП точный
Я уже знал, что добавление mutable решит проблему. Вопрос в том, что я хочу понять причину, по которой лямбда по умолчанию остается неизменной.
Я не адвокат по языкам, но это кажется мне очевидным: если вы сделаете operator()
не const
, вы не сможете сделать что-то как
template <typename F>
void foo (F const & f)
{ f(); }
// ...
foo([]{ std::cout << "lambda!" << std::endl; });
Я имею в виду... если operator()
не является const
, вы не можете использовать лямбды, передавая их в качестве const
ссылки.
А когда это строго не нужно, должно быть недопустимое ограничение.
Ответ 2
Нашел этот документ Хербом Саттером на 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 cant get more explicit
{ use( e, ++val ); }; // same error: count is const, need ‘mutable
Эта функция, кажется, была добавлена из опасения, что пользователь может не осознавать, что он получил копию, и, в частности, что, поскольку лямбды можно копировать, он может менять другую копию лямбды.
Выше цитаты и примеры показывают, почему Комитет по стандартам, возможно, сделали это const
по умолчанию и требуется mutable
, чтобы изменить его.
Ответ 3
Я думаю, это просто, чтобы избежать путаницы, когда переменная внутри лямбды ссылается не на то, что было первоначально захвачено. Лексически такая переменная как бы находится в области своего "оригинала". Копирование в основном позволяет продлить срок службы объекта. Когда захват не является копией, он ссылается на оригинал, и к оригиналу применяются модификации, и не возникает путаницы из-за двух разных объектов (один из которых вводится неявно), и это допускается оператором вызова функции лямбда-конста.