Lambda capture as const reference?
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 c++14 using static_cast
/ const_cast
:
[&best_string = static_cast<const std::string&>(best_string)](const string& s)
{
best_string = s; // fails
};
In c++17 using std::as_const
:
[&best_string = std::as_const(best_string)](const string& s)
{
best_string = s; // fails
};
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.
Related videos on Youtube
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, 2021Comments
-
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 over 13 yearsshouldn't your lambda look like:
[&, &best_string](string const s) { ...}
? -
Harshil Shukla about 12 yearsreally 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 over 10 yearslooking 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 over 8 yearsThis 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 over 13 yearsThe capture clause still mentions
best_string
only. Apart from that, GCC 4.5 "successfully rejects" the code like intended. -
John Dibling over 13 yearsYes, 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 over 13 yearsWhy would that make it a "non-lambda"?
-
Klaim over 13 yearsBecause 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 about 11 yearsI 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 wasconst
, the compiler would have enforced the correct behavior on the programmer. It'd be nice if the syntax supported[&mutableVar, const &constVar]
. -
leemes about 11 yearsNote 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 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 over 9 years"If the functor should be context-independant, make it a real functor" ... and kiss possible inlining goodbye?
-
Aaron McDaid over 8 yearsIt seems like this should be possible with C++14, but I can't get it to work. Any suggestions?
-
StenSoft over 8 yearsConstness is inherited from the captured variable. So if you want to capture
a
asconst
, declareconst auto &b = a;
before the lambda and captureb
-
Aaron McDaid over 8 yearsAlso, 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 over 8 years@AaronMcDaid
const_cast
can unconditionally change a volatile object to a const object (when asked to cast toconst
), thus, for adding constraints I preferstatic_cast
-
Grault over 8 yearsExcept 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 about 8 years@StenSoft Bleargh. Except apparently this doesn't apply when capturing a member-variable by reference:
[&foo = this->foo]
inside of aconst
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 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 almost 8 years@M.M
&basic_string = std::as_const(best_string)
should solve all problems -
Divyang Desai over 7 yearsWhile 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 over 7 yearsOk, I edited my answer to add gcc bug description here.
-
Stein over 7 yearsThis 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 about 7 yearsIf 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 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 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 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 about 6 yearsIts a very old question: your answer is lacking context related to which C++ version you are referring to. Please provide this content.
-
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 over 2 yearsNice. This requires
c++14
, and here's a more minimal example. -
Elliott over 2 yearsFor any readers, this seems to be fixed from gcc 7.1 onwards: godbolt.org/z/c8MnnY7xY
-
Elliott over 2 yearsYou can actually just write
best_string = std::cref(best_string)
fromc++14
onwards. See Sergey's answer for an explanation, or this example.