Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda capture as const reference?

People also ask

Is lambda capture a const?

By default, variables are captured by const value . This means when the lambda is created, the lambda captures a constant copy of the outer scope variable, which means that the lambda is not allowed to modify them.

Does lambda capture reference by value?

Lambdas always capture objects, and they can do so by value or by reference.

How do you call a lambda 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.

Can lambdas be Inlined?

Calls of the lambda are translated to direct calls to its operator() and can therefore be inlined.


In c++14 using static_cast / const_cast:

[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO


In c++17 using std::as_const:

[&best_string = std::as_const(best_string)](const string& s)
{
    best_string = s; // fails
};

DEMO 2


const isn't in the grammar for captures as of n3092:

capture:
  identifier
  & identifier
  this

The text only mention capture-by-copy and capture-by-reference and doesn't mention any sort of const-ness.

Feels like an oversight to me, but I haven't followed the standardization process very closely.


I think the capture part should not specify const, as the capture means, it only need a way to access the outer scope variable.

The specifier is better specified in the outer scope.

const string better_string = "XXX";
[&better_string](string s) {
    better_string = s;    // error: read-only area.
}

lambda function is const(can't change value in its scope), so when you capture variable by value, the variable can not be changed, but the reference is not in the lambda scope.


I guess if you're not using the variable as a parameter of the functor, then you should use the access level of the current function. If you think you shouldn't, then separate your lambda from this function, it's not part of it.

Anyway, you can easily achieve the same thing that you want by using another const reference instead :

#include <cstdlib>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main()
{
    string strings[] = 
    {
        "hello",
        "world"
    };
    static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);

    string best_string = "foo";
    const string& string_processed = best_string;

    for_each( &strings[0], &strings[num_strings], [&string_processed]  (const string& s)  -> void 
    {
        string_processed = s;    // this should fail
    }
    );
    return 0;
}

But that's the same as assuming that your lambda have to be isolated from the current function, making it a non-lambda.


There is a shorter way.

Note that there is no ampersand before "best_string".

It will be of a const std::reference_wrapper<T> type.

[best_string = std::cref(best_string)](const string& s)
{
    best_string = s; // fails
};

http://coliru.stacked-crooked.com/a/0e54d6f9441e6867


I think you have three different options:

  • don't use const reference, but use a copy capture
  • ignore the fact that it is modifiable
  • use std::bind to bind one argument of a binary function which has a const reference.

using a copy

The interesting part about lambdas with copy captures is that those are actually read only and therefore do exactly what you want them to.

int main() {
  int a = 5;
  [a](){ a = 7; }(); // Compiler error!
}

using std::bind

std::bind reduces the arity of a function. Note however that this might/will lead to an indirect function call via a function pointer.

int main() {
  int a = 5;
  std::function<int ()> f2 = std::bind( [](const int &a){return a;}, a);
}