Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a lambda's call-operator implicitly const?

I have a small "lambda expression" in the below function:

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

Below is the "anonymous closure class" generated for the above lambda expression.

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};
}

The closure's "operator()" generated by the compiler is implicitly const. Why did the standard committee make it const by default?

like image 784
cpp_enthusiast Avatar asked Nov 23 '18 11:11

cpp_enthusiast


People also ask

How do you call a lambda function in C++?

A lambda is also just a function object, so you need to have a () to call it, there is no way around it (except of course some function that invokes the lambda like std::invoke ). If you want you can drop the () after the capture list, because your lambda doesn't take any parameters.

What is mutable in lambda?

The mutable keyword is used so that the body of the lambda expression can modify its copies of the external variables x and y , which the lambda expression captures by value. Because the lambda expression captures the original variables x and y by value, their values remain 1 after the lambda executes.

What is capture list in C++?

The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).

Why do we need lambda expressions in C++?

One of the new features introduced in Modern C++ starting from C++11 is Lambda Expression. It is a convenient way to define an anonymous function object or functor. It is convenient because we can define it locally where we want to call it or pass it to a function as an argument.

What is a lambda expression in C++?

In C++11 and later, a lambda expression—often called a lambda —is a convenient way of defining an anonymous function object (a closure) right at the location where it is invoked or passed as an argument to a function. Typically lambdas are used to encapsulate a few lines of code that are passed to algorithms or asynchronous methods.

What are lambdas used for in programming?

Typically lambdas are used to encapsulate a few lines of code that are passed to algorithms or asynchronous functions. This article defines what lambdas are, and compares them to other programming techniques.

How do you initialize a variable in a lambda function?

In C++14, you can introduce and initialize new variables in the capture clause, without the need to have those variables exist in the lambda function’s enclosing scope. The initialization can be expressed as any arbitrary expression; the type of the new variable is deduced from the type produced by the expression.

Can you omit return type in lambda expression?

You can omit the return-type part of a lambda expression if the lambda body contains just one return statement or the expression does not return a value. If the lambda body contains one return statement, the compiler deduces the return type from the type of the return expression.


2 Answers

Found this paper by Herb Sutter at open-std.org which discusses this matter.

The odd couple: Capture by value’s injected const and quirky mutable
Consider this strawman example, where the programmer captures a local variable by value and tries to modify the captured value (which is a member variable of the lambda object):

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’

This feature appears to have been added out of a concern that the user might not realize he got a copy, and in particular that since lambdas are copyable he might be changing a different lambda’s copy.

The above quote and example indicate why the Standards Committee might have made it const by default and required mutable to change it.

like image 187
P.W Avatar answered Nov 05 '22 17:11

P.W


From cppreference

Unless the keyword mutable was used in the lambda-expression, the function-call operator is const-qualified and the objects that were captured by copy are non-modifiable from inside this operator()

In your case, there is nothing that, captured by copy, is modifiable.

I suppose that, if you write something as

int x = 10;

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

the const should disappear

-- EDIT --

The OP precise

I already knew that adding mutable will solve the issue. The question is that I want to understand the reason behind making the lambda immutable by default.

I'm not a language lawyer but this seems me obvious: if you make operator() not const, you can't make something as

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

// ...

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

I mean... if operator() isn't const, you can't use lambdas passing they as const reference.

And when isn't strictly needed, should be an unacceptable limitation.

like image 44
max66 Avatar answered Nov 05 '22 18:11

max66