Lambda capture as const reference?

82,690

Solution 1

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.

Solution 2

In using static_cast / const_cast:

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

DEMO


In using std::as_const:

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

DEMO 2

Solution 3

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.

Solution 4

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

Solution 5

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.

Share:
82,690

Related videos on Youtube

John Dibling
Author by

John Dibling

Here's what StackOverflow feels like to me some days. Here's a little joke that cracks me up: So, a guy walks up to me and says, "I'm a tepee. I'm a wigwam. I'm a tepee! I'm a wigwam!" I said to him, "Relax, man. You're two tents!"

Updated on December 10, 2021

Comments

  • John Dibling
    John Dibling over 2 years

    Is it possible to capture by const reference in a lambda expression?

    I want the assignment marked below to fail, for example:

    #include <algorithm>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        string strings[] = 
        {
            "hello",
            "world"
        };
        static const size_t num_strings = sizeof(strings)/sizeof(strings[0]);
    
        string best_string = "foo";
    
        for_each( &strings[0], &strings[num_strings], [&best_string](const string& s)
          {
            best_string = s; // this should fail
          }
        );
    return 0;
    }
    

    Update: As this is an old question, it might be good to update it if there are facilities in C++14 to help with this. Do the extensions in C++14 allow us to capture a non-const object by const reference? (August 2015)

    • erjot
      erjot over 13 years
      shouldn't your lambda look like: [&, &best_string](string const s) { ...}?
    • Harshil Shukla
      Harshil Shukla about 12 years
      really inconsistent capture. "const &" can be very useful when you have large const object which should be accessed but not modified in lambda function
    • Alexander Oh
      Alexander Oh over 10 years
      looking at the code. you could use a two parameter lambda and bind the second as a const reference. comes with a cost though.
    • Aaron McDaid
      Aaron McDaid over 8 years
      This isn't possible in C++11 it would seem. But perhaps we can update this question for C++14 - are there extensions that allow this? The C++14 generalized lambda captures?
  • sellibitze
    sellibitze over 13 years
    The capture clause still mentions best_string only. Apart from that, GCC 4.5 "successfully rejects" the code like intended.
  • John Dibling
    John Dibling over 13 years
    Yes, this would give me the results I was trying to achieve on a technical level. Ultimately however, the answer to my original question seems to be "no."
  • Admin
    Admin over 13 years
    Why would that make it a "non-lambda"?
  • Klaim
    Klaim over 13 years
    Because the nature of a lambda is that it's context-dependant. If you don't need a specific context then it's just a quick way to make a functor. If the functor should be context-independant, make it a real functor.
  • Sean
    Sean about 11 years
    I just tracked a bug back to a variable being modified from the capture that was mutable, but should have been const. Or more correctly, if the capture variable was const, the compiler would have enforced the correct behavior on the programmer. It'd be nice if the syntax supported [&mutableVar, const &constVar].
  • leemes
    leemes about 11 years
    Note that there is std::begin/end which also works for raw arrays and makes it consistent and more readable to get the iterators for (almost?) any type of sequence. Also, there now is the range-based for loop: for(auto variable : sequence)
  • zhb
    zhb over 10 years
    @Amarnath Balasubramani: It's just my opinion, I think there is no need to specify a const reference in lambda capture part, why should there is a variable const here and not const at another place(if that possible, it will be error-prone). happy to see your response anyway.
  • Andrew Lazarus
    Andrew Lazarus over 9 years
    "If the functor should be context-independant, make it a real functor" ... and kiss possible inlining goodbye?
  • Aaron McDaid
    Aaron McDaid over 8 years
    It seems like this should be possible with C++14, but I can't get it to work. Any suggestions?
  • StenSoft
    StenSoft over 8 years
    Constness is inherited from the captured variable. So if you want to capture a as const, declare const auto &b = a; before the lambda and capture b
  • Aaron McDaid
    Aaron McDaid over 8 years
    Also, perhaps this should be edited into the accepted answer? Either way, there should be one good answer that covers both c++11 and c++14. Although, I guess it could be argued that c++14 will be good enough for everyone over the coming years
  • Piotr Skotnicki
    Piotr Skotnicki over 8 years
    @AaronMcDaid const_cast can unconditionally change a volatile object to a const object (when asked to cast to const), thus, for adding constraints I prefer static_cast
  • Grault
    Grault over 8 years
    Except changes to the variable in the containing scope will not be reflected in the lambda. It's not a reference, it's just a variable which shouldn't be reassigned because the reassignment wouldn't mean what it would appear to mean.
  • Kyle Strand
    Kyle Strand about 8 years
    @StenSoft Bleargh. Except apparently this doesn't apply when capturing a member-variable by reference: [&foo = this->foo] inside of a const function gives me an error stating that the capture itself discards qualifiers. This could be a bug in GCC 5.1, though, I suppose.
  • M.M
    M.M almost 8 years
    @PiotrSkotnicki on the other hand, static_cast to const reference may silently create a temporary if you didn't get the type exactly right
  • Piotr Skotnicki
    Piotr Skotnicki almost 8 years
    @M.M &basic_string = std::as_const(best_string) should solve all problems
  • Divyang Desai
    Divyang Desai over 7 years
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.”
  • user1448926
    user1448926 over 7 years
    Ok, I edited my answer to add gcc bug description here.
  • Stein
    Stein over 7 years
    This is quite an indirect answer the question, if any. The bug is about how a compiler fails when capturing something const, so perhaps why some way to address or work around the issue in the question might not work with gcc.
  • Jonathan Sharman
    Jonathan Sharman about 7 years
    If you need to modify better_string within the containing scope, then this solution won't work. The use case for capturing as const-ref is when the variable needs to be mutable in the containing scope but not within the lambda.
  • Steed
    Steed over 6 years
    @JonathanSharman, it doesn't cost you anything to create a const reference to a variable, so you can make a const string &c_better_string = better_string; and happily pass it to the lambda: [&c_better_string]
  • Jonathan Sharman
    Jonathan Sharman over 6 years
    @Steed The problem with that is you're introducing an extra variable name into the surrounding scope. I think Piotr Skotnicki's solution above is the cleanest, as it achieves const-correctness while keeping variable scopes minimal.
  • Steed
    Steed over 6 years
    @JonathanSharman, here we enter the land of opinions - what is the prettiest, or cleanest, or whatever. My point is is that both solutions are suitable for the task.
  • ZF007
    ZF007 about 6 years
    Its a very old question: your answer is lacking context related to which C++ version you are referring to. Please provide this content.
  • Kyle Strand
    Kyle Strand over 5 years
    @PiotrSkotnicki Except the problem of that being a hideous way to write something that should be as simple as const& best_string.
  • Elliott
    Elliott over 2 years
    Nice. This requires c++14, and here's a more minimal example.
  • Elliott
    Elliott over 2 years
    For any readers, this seems to be fixed from gcc 7.1 onwards: godbolt.org/z/c8MnnY7xY
  • Elliott
    Elliott over 2 years
    You can actually just write best_string = std::cref(best_string) from c++14 onwards. See Sergey's answer for an explanation, or this example.