This question already has answers here:
C++11 lambda implementation and memory model
(2 answers)
Closed 7 years ago.
Where do lambdas and std::functions store variables captured by value?
int i = 1;
auto l = [i](void) mutable { return i++; };
std::function<int(void)> f = l;
Do they call the new operator? If I provide my own new operator, will it be used by lambdas?
From [expr.prim.lambda] 5.1.2(15)
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that is not of the form & identifier or & identifier initializer. For each entity captured by copy, an unnamed non-static data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise. [ Note: If the
captured entity is a reference to a function, the corresponding data member is also a reference to a function. —end note ] A member of an anonymous union shall not be captured by copy.
emphasis mine
So a variable captured by value will be stored as member of the closure type. The closure type is unspecified as per 5.1.2(3)
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type — called the closure type — whose properties are described below. This class type is neither an aggregate (8.5.1) nor a literal type (3.9). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
At compile time lambdas are actually turned into classes. So if you had the following.
int a; Widget widget;
auto somelambda = [=a,&widget](){
}
You could actually think of it as
class SomeLambda
{
int a;
Widge& w;
...
}
Which should make you really cautious because you can have dangling references.
Related
I already know automatic move is not woking with the function which return value from Rvalue Reference input. But why?
Below is example code which automatic move is not working with.
Widget makeWidget(Widget&& w) {
....
return w; // Compiler copies w. not move it.
}
If the function input is from copy by value, automatic move works.
Widget makeWidget(Widget w) {
....
return w; // Compiler moves w. not copy it.
}
The code should work as you expected since C++20; automatic move operation is performed when returning local variables and parameters, and it works for rvalue reference too (since C++20).
If expression is a (possibly parenthesized) id-expression that names a
variable whose type is either
a non-volatile object type or
a non-volatile rvalue reference to object type (since C++20)
LIVE
Relevant part of the C++17 standard [class.copy.elision/3]:
In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
If the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function...
This does not hold for:
Widget makeWidget(Widget&& w)
{
return w;
}
since w does not name an object with automatic storage duration (within the function).
In C++20, the situation is different:
An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
If the expression in a return or co_return statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function...
Here, the reference w satisfies those requirements since it is an rvalue reference declared in the parameter-declaration-clause.
Why the same was not until C++20? I cannot say; likely nobody proposed such a change before.
Consider this piece of code:
class shy {
private:
int dont_touch; // Private member
public:
static const shy object;
};
const shy shy::object = []{
shy obj;
obj.dont_touch = 42; // Accessing a private member; compiles; WHY?
return obj;
}();
int main()
{
}
Live code (Clang)
Live code (GCC)
It seems really unintuitive to me. What does the C++11/14 standard say about this? Is this a GCC/Clang bug?
As already answered in the comments #Tony D and #dyp:
§ 9.4.2/2 Static data members [class.static.data]:
The initializer expression in the
definition of a static data member is in the scope of its class.
The above means that static data members and their initializers can access other private and protected members of their class.
Also consdering the standard § 5.1.2/2&3 Lambda expressions [expr.prim.lambda]:
2 The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A closure object behaves like a function object (20.9).-end note]
3 The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion class type - called the closure type - whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
Combining the above we end up to the conclusion that the prvalue temporary closure object of your lambda is declared and defined in the initializer of the static const data member shy::object and consequently the scope of the lambda's closure object is the scope of class shy. As such it can access private members of class shy.
Given the following sample code:
int main()
{
int i;
auto f = [=]()mutable->int*
{
return &i;
};
return 0;
}
g++ v.4.8.1 warns that "address of local variable ‘i’ returned".
Clang v.3.2 (MacOS's Clang) warns that "address of stack memory
associated with local variable 'i' returned".
Neither VS2012 nor VS2013 RC warn of anything.
My understanding of lambdas is that the compiler will generate a functor class. That functor class will have members for all copied variables (i in the example). I believe that in the context of my code, as long as f exists, it is safe to return the address of one of its members. It seems to me that all compilers got it wrong. I think a warning about using the address of f's member i after f goes out of scope is valid, but the warnings about the "local variable 'i'" are incorrect/misleading. Am I right?
Yes, you're right. The & operator is applying to the member, not the local object.
Demonstration is straightforward: just modify your example to output the addresses.
#include <iostream>
int main() {
int i;
std::cout << & i << '\n';
std::cout << [=]() mutable -> int * {
return & i;
} () << '\n';
}
http://ideone.com/OqsDyg
Incidentally, this compiles with no warnings under -Wall in GCC 4.9.
Some terminology:
The = or & inside [&](){ /*..*/ } is called a capture-default.
odr-use of a variable roughly means that the variable does not appear in a discarded-value-expression (such as (void)some_variable or int x = some_variable, 5;) and it doesn't occur in a constant expression.
compound-statement is the "function block" { statements }
the name of a variable is an id-expression
[expr.prim.lambda]/3
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type — called the closure type — whose properties are described below.
/11
If a lambda-expression has an associated capture-default and its compound-statement odr-uses (3.2) this or a variable with automatic storage duration and the odr-used entity is not explicitly captured, then the odr-used entity is said to be implicitly captured;
Therefore, i is implicitly captured.
/14
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed non-static data member is declared in the closure type.
There is a non-static data member (of type int) in the closure type.
/17
Every id-expression that is an odr-use (3.2) of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.
We don't even need to interpret this, as this paragraph provides us with an example very similar to the OP's:
void f(const int*);
void g() {
const int N = 10;
[=] {
int arr[N]; // OK: not an odr-use, refers to automatic variable
f(&N); // OK: causes N to be captured; &N points to the
// corresponding member of the closure type
};
}
If we apply this to the OP's example, we see that &i refers to the internal non-static data member of the closure type. Whether or not the diagnostic message is appropriate is not specified in the Standard ;)
This question already has answers here:
What is the type of lambda when deduced with "auto" in C++11?
(8 answers)
Closed 6 years ago.
There is this code:
auto fun = [](int x)->int {return x + 1; };
std::cout << typeid(fun).name() << std::endl;
The result is: Z4mainEUliE_ but c++filt doesn't seem to explain what is it. What is type of lambda expression?
§5.1.2/3 states:
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type
It goes on to say more, but that's the most important bit. A lambda is basically an instance of an anonymous class.
Incidentally, the demangled form of your lambda is main::$_0.
The type of a lambda function is unspecified by the standard (§5.1.2):
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union classtype — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
It then goes on listing the exact properties a closure type should have.
Therefore there is no general type for a lambda function to have. The compiler will generate a new functor type with unspecified name for each lambda function
What is type of lambda expression?
The type of a lambda expression (the so-called closure) is an unnamed class type with a function call operator automatically generated by the compiler. The internal name the compiler will give it is unspecified.
According to Paragraph 5.1.2/3 of the C++11 Standard:
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed nonunion
class type — called the closure type — whose properties are described below. This class type is not
an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace
scope that contains the corresponding lambda-expression. [...]
Also notice, that the name() member function of the type_info class (the type returned by typeid()) is also implementation-dependent, and the Standard does not require it to be meaningful for a human.
Per Paragraph 18.7.1:
const char* name() const noexcept;
9 Returns: An implementation-defined NTBS.
10 Remarks: The message may be a null-terminated multibyte string (17.5.2.1.4.2), suitable for conversion and display as a wstring (21.3, 22.4.1.4)
For foo declared as Foo& foo = ...;, is there any difference between capture-by-value and capture-by-reference semantics for lambdas?
I think you have fallen to a common misconception... references are aliases to real objects. After initialization, any use of the reference is exactly equivalent to an use of the original object. If you consider this, the question makes little sense. If the reference is the object, then the behavior of [foo](){} will be exactly the same regardless of whether foo is an object or a reference to the object.
Yes, there is a difference.
§5.1.2 [expr.prim.lambda] p14
An entity is captured by copy if it is implicitly captured and the capture-default is = or if it is explicitly captured with a capture that does not include an &. For each entity captured by copy, an unnamed nonstatic data member is declared in the closure type. The declaration order of these members is unspecified. The type of such a data member is the type of the corresponding captured entity if the entity is not a reference to an object, or the referenced type otherwise.
So, if you capture an identifier that names a reference by value, you get a copy of the referenced object.