Why can't I capture this by-reference ('&this') in lambda?
The reason [&this]
doesn't work is because it is a syntax error. Each comma-seperated parameter in the lambda-introducer
is a capture
:
capture:
identifier
& identifier
this
You can see that &this
isn't allowed syntactically. The reason it isn't allowed is because you would never want to capture this
by reference, as it is a small const pointer. You would only ever want to pass it by value - so the language just doesn't support capturing this
by reference.
To capture this
explicitly you can use [this]
as the lambda-introducer
.
The first capture
can be a capture-default
which is:
capture-default:
&
=
This means capture automatically whatever I use, by reference (&
) or by value (=
) respectively - however the treatment of this
is special - in both cases it is captured by value for the reasons given previously (even with a default capture of &
, which usually means capture by reference).
5.1.2.7/8:
For purposes of name lookup (3.4), determining the type and value of
this
(9.3.2) and transforming id-
expressions referring to non-static class members into class member access expressions using(*this)
(9.3.1),
the compound-statement [OF THE LAMBDA] is considered in the context of the lambda-expression.
So the lambda acts as if it is part of the enclosing member function when using member names (like in your example the use of the name x
), so it will generate "implicit usages" of this
just like a member function does.
If a lambda-capture includes a capture-default that is
&
, the identifiers in the lambda-capture shall not be
preceded by&
. If a lambda-capture includes a capture-default that is=
, the lambda-capture shall not contain
this
and each identifier it contains shall be preceded by&
. An identifier orthis
shall not appear more than
once in a lambda-capture.
So you can use [this]
, [&]
, [=]
or [&,this]
as a lambda-introducer
to capture the this
pointer by value.
However [&this]
and [=, this]
are ill-formed. In the last case gcc forgivingly warns for [=,this]
that explicit by-copy capture of ‘this’ redundant with by-copy capture default
rather than errors.
Why capturing by reference in lambda doesn't change the type of the variables?
I thought if user captures by reference, new variables with the same name are introduced to the local scope to have reference type of the original variables.
The standard says (emphasis mine):
An entity is captured by reference if it is implicitly or explicitly captured but not captured by copy. It is unspecified whether additional unnamed non-static data members are declared in the closure type for entities captured by reference. If declared, such non-static data members shall be of literal type.
Therefore your expectations were wrong. Capturing by reference doesn't mean create a local reference to an object in the outer scope.
As a side note, consider the following:
int x;
auto l = [&x](){ return [&x](){} }();
If your expectations were right, x
would have been a reference to a reference within the lambda l
, that is something that doesn't exist in C++.
C++ lambda capture this vs capture by reference
For the specific example you've provided, capturing by this
is what you want. Conceptually, capturing this
by reference doesn't make a whole lot of sense, since you can't change the value of this
, you can only use it as a pointer to access members of the class or to get the address of the class instance. Inside your lambda function, if you access things which implicitly use the this
pointer (e.g. you call a member function or access a member variable without explicitly using this
), the compiler treats it as though you had used this
anyway. You can list multiple captures too, so if you want to capture both members and local variables, you can choose independently whether to capture them by reference or by value. The following article should give you a good grounding in lambdas and captures:
https://crascit.com/2015/03/01/lambdas-for-lunch/
Also, your example uses std::function
as the return type through which the lambda is passed back to the caller. Be aware that std::function
isn't always as cheap as you may think, so if you are able to use a lambda directly rather than having to wrap it in a std::function
, it will likely be more efficient. The following article, while not directly related to your original question, may still give you some useful material relating to lambdas and std::function
(see the section An alternative way to store the function object, but the article in general may be of interest):
https://crascit.com/2015/06/03/on-leaving-scope-part-2/
I can't pass lambda with reference capture
You can only do the above with capture-less lambdas.
See [expr.prim.lambda.closure] (sec 7)
The closure type for a non-generic lambda-expression with no
lambda-capture whose constraints (if any) are satisfied has a
conversion function to pointer to function with C++ language linkage
having the same parameter and return types as the closure type's
function call operator.
Since lambdas are not just ordinary functions and capturing it need to preserve a state,
you can not find any simple or conventional solution to make them assign to function pointers.
To fix, you can use std::function
which will do it by type erasure:
#include <functional> // std::function
int test;
std::function<float(int)> f = [&](int i) -> float {return static_cast<float>(test); };
C++11/14/17 Lambda reference capture [&] doesn't copy [*this]
[=]
,[this]
,[=, this]
and[&, this]
all capturesthis
by value. That is, it copies the value of the pointer that isthis
.[&]
captures*this
by reference. That is,this
in the lambda is a pointer to*this
outside the lambda.
The effect of the above versions with regards to this
in the lambda will therefore be the same.
[=, *this]
copies all elements captured and also makes a copy of*this
- not thethis
pointer.[&, *this]
makes a reference to all elements captured but makes a copy of*this
.
LambdaCapture.cpp:25:13: error: read-only variable is not assignable
++(this->mI);
^ ~~~~~~~~~~
That's because lambdas are const
by default. You need to make them mutable
in order to be able to change them.
auto f1 = [&, *this]() mutable { // made mutable
cout << mI << '\n';
++(this->mI); // now ok
};
C++ lambda capture list by value or by reference doesn't give me different results
This kind of confusion iss probably one of the reasons why C++20 deprecated the implicit capture of this
with [=]
. You can still capture [this]
, in which case you have the usual lifetime issues with an unmanaged pointer. You can capture [*this]
(since C+=17), which will capture a copy of *this
so you don't have lifetime issues.
You could also use std::enable_shared_from_this
since you're using a std::shared_ptr
in example
, but that's a bit more complicated. Still, it would avoid both the copy and the UB when the lifetime issues.
Related Topics
Why Do We Need to Use Boost::Asio::Io_Service::Work
What Is the Optimal Algorithm for Generating an Unbiased Random Integer Within a Range
May Std::Vector Make Use of Small Buffer Optimization
How to Call MAChine Code Stored in Char Array
Is Std::String Ref-Counted in Gcc 4.X/C++11
Check If a Variable Type Is Iterable
Convert Wstring to String Encoded in Utf-8
Why Should the Assignment Operator Return a Reference to the Object
Does Moving Leave the Object in a Usable State
Does Std::Vector *Have* to Move Objects When Growing Capacity? Or, Can Allocators "Reallocate"
Default Class Inheritance Access
Why Don't the C or C++ Standards Explicitly Define Char as Signed or Unsigned
Aliasing T* with Char* Is Allowed. Is It Also Allowed the Other Way Around
How to Detect Existence of a Class Using Sfinae
Bjarne Stroustrup Says We Must Avoid Linked Lists
Why How to Not Brace Initialize a Struct Derived from Another Struct