Lambda: uncaptured objects in unevaluated context - c++

I'd appreciate a pointer to the Standard paragraph stating that the following code is well-formed:
int main() {
int var = 0;
return [] { return sizeof(var); }(); // var is not captured!
}
Similar example appears e.g. in § 8.4.5.2, but I could not find any verbal description of it.

It's specified in terms of when an entity must be captured, rather than in terms of when it may not.
[expr.prim.lambda.capture] (with some omissions)
8 ... If a lambda-expression or an instantiation of the function
call operator template of a generic lambda odr-uses this or a variable
with automatic storage duration from its reaching scope, that entity
shall be captured by the lambda-expression. ...
[ Example:
void f1(int i) {
int const N = 20;
auto m1 = [=]{
int const M = 30;
auto m2 = [i]{
int x[N][M]; // OK: N and M are not odr-used
// ...
};
};
// ...
}
— end example ]
The key in your code sample is that var is not odr-used, because it is an unevaluated operand. As such, it does not need to be captured.

Related

Does the default argument for a function parameter is considered as an initializer for that parameter?

Suppose I have function declarations like these:
static const int R = 0;
static const int I = 0;
void f(const int& r = R);
void g(int i = I);
Per [dcl.fct.default]/1:
If an initializer-clause is specified in a parameter-declaration this
initializer-clause is used as a default argument [..]
and per the grammar constructs, an initializer can comprise an initializer-clause. Right?
So I concluded that R is an initializer for the parameter r, and I is also an initializer for the parameter i.
Now per [const.expr]/2:
A variable or temporary object o is constant-initialized if
(2.1) either it has an initializer [..] and
(2.2) the full-expression of its initialization is a constant expression [..]
So both parameters have an initializer and also the full-expression of their initializations is a constant expression.
So, Are both parameters r and i considered constant-initialized?
No, because an initializer-clause is not necessarily part of an initializer. The grammar item in question here is this version of the parameter-declaration:
attribute-specifier-seqopt thisopt decl-specifier-seq declarator = initializer-clause
Applying just the right amount of hair-splitting, this means that the form of a parameter declaration with a default argument is similar to an initialization, but it is still subtly different. The most obvious difference being that the default argument is ignored if an actual argument is used. Less obvious is that a parameter cannot actually be constant-initialized, even if its default argument is a constant expression. This remains true even if the function is only evaluated at compile time. Here is some code that shows the subtle differences in meaning:
#include <random>
// R may be evaluated at compile time and must be constant-initialized
static constexpr int R = 0;
// f may be evaluated at compile time
constexpr int f(const int& r = R) { return r + 42; }
// I must be constant- *or* zero-initialized, even if only evaluated at runtime
constinit const int I = f();
// g is an "immediate function": it may not be evaluated at runtime
consteval int g(int i = I) { return i - 42; }
int main() {
// A variable that may not appear in constant expressions,
// because it is not constant-initialized
const int not_const = std::rand();
static int x1 = f(); // OK, constant initialization
// constexpr int x2 = f(not_const); // error, not constant-initialized
int x3 = f(); // OK, f() evaluated at compile time
int x4 = f(not_const); // OK, evaluated at runtime
static int y1 = g(); // OK
// constexpr int y2 = g(not_const); // error
int y3 = g(); // OK
// int y4 = g(not_const); // error
}
As you can see here, this generates only one runtime call to f(int const&) and none to g(int). Even though the parameters for the other evaluations were effectively compile-time constants, you can neither use nor specify them as such:
constexpr int f(const int& r = R) {
// constexpr int x = r; // error, not a constant expression
return r + 42;
}
consteval int g(int i = I) {
// constexpr int y = i; // still not a constant expression!
return i - 42;
}
// Also not allowed:
// consteval void h(constexpr int x = 0, constinit int y = 1);

C++ Lambda Code Generation with Init Captures in C++ 14

I am trying to understand / clarify the code code that is generated when captures are passed to lambdas especially in generalized init captures added in C++14.
Give the following code samples listed below this is my current understanding of what the compiler will generate.
Case 1: capture by value / default capture by value
int x = 6;
auto lambda = [x]() { std::cout << x << std::endl; };
Would equate to:
class __some_compiler_generated_name {
public:
__some_compiler_generated_name(int x) : __x{x}{}
void operator()() const { std::cout << __x << std::endl;}
private:
int __x;
};
So there are multiple copies, one to copy into the constructor parameter and one to copy into the member, which would be expensive for types like vector etc.
Case 2: capture by reference / default capture by reference
int x = 6;
auto lambda = [&x]() { std::cout << x << std::endl; };
Would equate to:
class __some_compiler_generated_name {
public:
__some_compiler_generated_name(int& x) : x_{x}{}
void operator()() const { std::cout << x << std::endl;}
private:
int& x_;
};
The parameter is a reference and the member is a reference so no copies. Nice for types like vector etc.
Case 3:
Generalised init capture
auto lambda = [x = 33]() { std::cout << x << std::endl; };
My under standing is this is similar to Case 1 in the sense
that it is copied into to the member.
My guess is that the compiler generates code similar to...
class __some_compiler_generated_name {
public:
__some_compiler_generated_name() : __x{33}{}
void operator()() const { std::cout << __x << std::endl;}
private:
int __x;
};
Also if I have the following:
auto l = [p = std::move(unique_ptr_var)]() {
// do something with unique_ptr_var
};
What would the constructor look like? Does it also move it into the member?
Case 1 [x](){}: The generated constructor will accept its argument by possibly const-qualified reference to avoid unnecessary copies:
__some_compiler_generated_name(const int& x) : x_{x}{}
Case 2 [x&](){}: Your assumptions here are correct, x is passed and stored by reference.
Case 3 [x = 33](){}: Again correct, x is initialized by value.
Case 4 [p = std::move(unique_ptr_var)]: The constructor will look like this:
__some_compiler_generated_name(std::unique_ptr<SomeType>&& x) :
x_{std::move(x)}{}
so yes, the unique_ptr_var is "moved into" the closure. See also Scott Meyer's Item 32 in Effective Modern C++ ("Use init capture to move objects into closures").
There's less of a need to speculate, using cppinsights.io.
Case 1:
Code
#include <memory>
int main() {
int x = 33;
auto lambda = [x]() { std::cout << x << std::endl; };
}
Compiler generates
#include <iostream>
int main()
{
int x = 6;
class __lambda_5_16
{
int x;
public:
inline void operator()() const
{
std::cout.operator<<(x).operator<<(std::endl);
}
// inline /*constexpr */ __lambda_5_16(const __lambda_5_16 &) = default;
// inline /*constexpr */ __lambda_5_16(__lambda_5_16 &&) noexcept = default;
public: __lambda_5_16(int _x)
: x{_x}
{}
};
__lambda_5_16 lambda = __lambda_5_16(__lambda_5_16{x});
}
Case 2:
Code
#include <iostream>
#include <memory>
int main() {
int x = 33;
auto lambda = [&x]() { std::cout << x << std::endl; };
}
Compiler generates
#include <iostream>
int main()
{
int x = 6;
class __lambda_5_16
{
int & x;
public:
inline void operator()() const
{
std::cout.operator<<(x).operator<<(std::endl);
}
// inline /*constexpr */ __lambda_5_16(const __lambda_5_16 &) = default;
// inline /*constexpr */ __lambda_5_16(__lambda_5_16 &&) noexcept = default;
public: __lambda_5_16(int & _x)
: x{_x}
{}
};
__lambda_5_16 lambda = __lambda_5_16(__lambda_5_16{x});
}
Case 3:
Code
#include <iostream>
int main() {
auto lambda = [x = 33]() { std::cout << x << std::endl; };
}
Compiler generates
#include <iostream>
int main()
{
class __lambda_4_16
{
int x;
public:
inline void operator()() const
{
std::cout.operator<<(x).operator<<(std::endl);
}
// inline /*constexpr */ __lambda_4_16(const __lambda_4_16 &) = default;
// inline /*constexpr */ __lambda_4_16(__lambda_4_16 &&) noexcept = default;
public: __lambda_4_16(int _x)
: x{_x}
{}
};
__lambda_4_16 lambda = __lambda_4_16(__lambda_4_16{33});
}
Case 4 (unofficially):
Code
#include <iostream>
#include <memory>
int main() {
auto x = std::make_unique<int>(33);
auto lambda = [x = std::move(x)]() { std::cout << *x << std::endl; };
}
Compiler generates
// EDITED output to minimize horizontal scrolling
#include <iostream>
#include <memory>
int main()
{
std::unique_ptr<int, std::default_delete<int> > x =
std::unique_ptr<int, std::default_delete<int> >(std::make_unique<int>(33));
class __lambda_6_16
{
std::unique_ptr<int, std::default_delete<int> > x;
public:
inline void operator()() const
{
std::cout.operator<<(x.operator*()).operator<<(std::endl);
}
// inline __lambda_6_16(const __lambda_6_16 &) = delete;
// inline __lambda_6_16(__lambda_6_16 &&) noexcept = default;
public: __lambda_6_16(std::unique_ptr<int, std::default_delete<int> > _x)
: x{_x}
{}
};
__lambda_6_16 lambda = __lambda_6_16(__lambda_6_16{std::unique_ptr<int,
std::default_delete<int> >
(std::move(x))});
}
And I believe this last piece of code answers your question. A move occurs, but not [technically] in the constructor.
Captures themselves aren't const, but you can see that the operator() function is. Naturally, if you need to modify the captures, you mark the lambda as mutable.
This question cannot be fully answered in code. You might be able to write somewhat "equivalent" code, but the standard is not specified that way.
With that out of the way, let's dive into [expr.prim.lambda]. The first thing to note is that constructors are only mentioned in [expr.prim.lambda.closure]/13:
The closure type associated with a lambda-expression has no default constructor if the lambda-expression has a lambda-capture and a defaulted default constructor otherwise. It has a defaulted copy constructor and a defaulted move constructor ([class.copy.ctor]). It has a deleted copy assignment operator if the lambda-expression has a lambda-capture and defaulted copy and move assignment operators otherwise ([class.copy.assign]). [ Note: These special member functions are implicitly defined as usual, and might therefore be defined as deleted. — end note ]
So right off the bat, it should be clear that constructors are not formally how capturing objects is defined. You can get pretty close (see the cppinsights.io answer), but the details differ (note how the code in that answer for case 4 does not compile).
These are the main standard clauses needed to discuss case 1:
[expr.prim.lambda.capture]/10
[...]
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 referenced type if the entity is a reference to an object, an lvalue reference to the referenced function type if the entity is a reference to a function, or the type of the corresponding captured entity otherwise.
A member of an anonymous union shall not be captured by copy.
[expr.prim.lambda.capture]/11
Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type. [...]
[expr.prim.lambda.capture]/15
When the lambda-expression is evaluated, the entities that are captured by copy are used to direct-initialize each corresponding non-static data member of the resulting closure object, and the non-static data members corresponding to the init-captures are initialized as indicated by the corresponding initializer (which may be copy- or direct-initialization). [...]
Let's apply this to your case 1:
Case 1: capture by value / default capture by value
int x = 6;
auto lambda = [x]() { std::cout << x << std::endl; };
The closure type of this lambda will have an unnamed non-static data member (let's call it __x) of type int (since x is neither a reference nor a function), and accesses to x within the lambda body are transformed to accesses to __x. When we evaluate the lambda expression (i.e. when assigning to lambda), we direct-initialize __x with x.
In short, only one copy takes place. The constructor of the closure type is not involved, and it is not possible to express this in "normal" C++ (note that the closure type is not an aggregate type either).
Reference capture involve [expr.prim.lambda.capture]/12:
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. [...]
There is another paragraph about reference capture of references but we're not doing that anywhere.
So, for case 2:
Case 2: capture by reference / default capture by reference
int x = 6;
auto lambda = [&x]() { std::cout << x << std::endl; };
We don't know whether a member is added to the closure type. x in the lambda body might just directly refer to the x outside. This is up to the compiler to figure out, and it will do this in some form of intermediate language (which differs from compiler to compiler), not a source transformation of the C++ code.
Init captures are detailed in [expr.prim.lambda.capture]/6:
An init-capture behaves as if it declares and explicitly captures a variable of the form auto init-capture ; whose declarative region is the lambda-expression's compound-statement, except that:
(6.1)
if the capture is by copy (see below), the non-static data member declared for the capture and the variable are treated as two different ways of referring to the same object, which has the lifetime of the non-static data member, and no additional copy and destruction is performed, and
(6.2)
if the capture is by reference, the variable's lifetime ends when the closure object's lifetime ends.
Given that, let's look at case 3:
Case 3: Generalised init capture
auto lambda = [x = 33]() { std::cout << x << std::endl; };
As stated, imagine this as a variable being created by auto x = 33; and explicitly captured by copy. This variable is only "visible" within the lambda body. As noted in [expr.prim.lambda.capture]/15 earlier, the initialization of the corresponding member of the closure type (__x for posterity) is by the given initializer upon evaluation of the lambda expression.
For the avoidance of doubt: This does not mean things are initialized twice here. The auto x = 33; is an "as if" to inherit the semantics of simple captures, and the described initialization is a modification to those semantics. Only one initialization happens.
This also covers case 4:
auto l = [p = std::move(unique_ptr_var)]() {
// do something with unique_ptr_var
};
The closure type member is initialized by __p = std::move(unique_ptr_var) when the lambda expression is evaluated (i.e. when l is assigned to). Accesses to p in the lambda body are transformed into accesses to __p.
TL;DR: Only the minimal number of copies/initializations/moves are performed (as one would hope/expect). I would assume that lambdas are not specified in terms of a source transformation (unlike other syntactic sugar) exactly because expressing things in terms of constructors would necessitate superfluous operations.
I hope this settles the fears expressed in the question :)

Are local class rules aligned to c++14 return type deduction?

While reading this part of C++14 (a free draft N4141, closest to C++14):
9.8 Local class declarations [class.local]
[..]
The name of a local class is local to its enclosing scope. [..]
Declarations in a local class shall not odr-use (3.2) a variable with
automatic storage duration from an enclosing scope. [ Example:
//[..]
void f()
{
static int s ;
int x;
// [..]
struct local {
int g() { return x; } // error: odr-use of automatic variable x
int h() { return s; } // OK
// [..]
};
}
local* p = 0; // error: local not in scope
—end example ]
I noticed, that, first - I can define p with return value auto deduction:
auto f()
{
static int s;
int x;
struct local
{
int h() { return s; }
};
return local{};
}
decltype(f())* p = 0; // OK - ignored, that local is not in scope!
Maybe it seems ok, why not to use local type by deducing it from function return value, but - it seems in this way I can access local s variable before it is constructed:
struct TalkativeInt
{
TalkativeInt() : value()
{
std::cout << "TalkativeInt()\n";
}
TalkativeInt(int value) : value(value)
{
std::cout << "TalkativeInt(" << value << ")\n";
}
int value;
};
auto f()
{
static TalkativeInt s = 7;
int x;
struct local
{
auto h() { return s.value; }
};
return local{};
}
decltype(f())* p = 0;
int main() {
decltype(f()) l;
std::cout << l.h();
}
Output is just::
0
But one might expect:
TalkativeInt(7)
7
Both clang and gcc do not protest in any way, see demo.
I wonder, maybe such case should be mentioned somehow either in
9.8 Local class declarations [class.local]
or in
7.1.6.4 auto specifier [dcl.spec.auto]
?
Of course, I feel that reading (and writing) from variable before it is constructed is bad thing, but I have not found anything about that in standard - probably, it was not possible before C++14? Or there are some basic rules I just overlooked?
The rule for static local variables is plain and simple:
Dynamic initialization of a block-scope variable with static storage
duration (3.7.1) or thread storage duration (3.7.2) is performed the
first time control passes through its declaration; such a variable is
considered initialized upon the completion of its initialization.
And accessing an object's members before its construction is disallowed by [basic.life]/(7.1).

Declaring a function using auto, decltype, = , [ ]

I found a function defined this way on:
http://en.cppreference.com/w/cpp/language/decltype
I have never seen this syntax used to defined a function, can anyone explain this?
This only seems to work with auto and decltype
#include <iostream>
using namespace std;
auto f = [](int a, int b)->int
{
return a * b;
};
/*
int f = [](int a, int b) //DOES NOT WORK
{
return a * b;
};
*/
int main()
{
int a = 2, b = 3;
cout<<f(a,b);
return 0;
}
I'm not certain if the following function uses dectype when we do:
->int
If it does, then how?
auto f = [](int a, int b)->int
{
return a * b;
};
The f here is a global variable of some anonymous type, holding an instance of the anonymous function object defined after the operator=. Such anonymous function objects are called lambdas, they can occur everywhere where you can have some expression:
int main(int, char**) {
([](int a) {
cout << "lambda called with " << a << endl;
})(42);
return ([](int a, int b) { return a - 2 * b; })(42, 21);
}
The general syntax of such a lambda expression is the following:
"["CAPTURE"]" PARAMETERS ["mutable"] [RETURN] { BODY }
CAPTURE being a list of zero ore more of
variable from the enclosing scope, capture by value
variable from the enclosing scope, preceeded by &, capture by reference
& means capture all variables from the enclosing scope by reference
= means capture all variables from the enclosing scope by value
PARAMETERS are the usual parameter lists you know from functions, optionally (since C++14) with auto and type deduction.
mutable allows the lambda to mutate its captured variables.
The optional RETURN contains a specification of the return type, e.g. -> void and the BODY contains arbitrary expressions and statements.
Note that this is only a rough sketch of the syntax, but it should get you started. You can find out more about lambdas in the standard, searching for "C++11 lambda" on Google or for example here.
Btw, a lambda is nothing spooky, you can think of your f as being a rough equivalent of the following, "old style" C++ code:
struct {
int operator()(int a, int b) const {
return a * b;
}
} f;
When you use:
auto f = [](int a, int b)->int
{
return a * b;
};
the type of f is type of the lambda expression. It is not int, the return type of the lambda expression.
As far as the -> int part goes, that's the only syntax available to explicitly specify the return type of a lambda expression. It that is omitted, the return type is deduced by the compiler using an algorithm specified by the standard:
5.1.2 Lambda expressions
4 If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the
following type:
— if the compound-statement is of the form
{ attribute-specifier-seqopt return expression ; }
the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion(4.2), and function-to-pointer conversion (4.3);
— otherwise, void.
[ Example:
auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
// braced-init-list is not an expression)
—end example ]

Generic lambda with std::function does not capture variables

I'm trying to use the generic lambda of C++14, but got a trouble with std::function.
#include <iostream>
#include <functional>
int main()
{
const int a = 2;
std::function<void(int)> f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
This fails to compile with an error message saying that error: ‘a’ was not declared in this scope.
It works if I change it to (int b).
Is it a bug? or am I missing something?
The version of GCC i'm using is 4.9.2.
I can reproduce this unless I do any of the following:
remove const from a
name a in the capture-list
change std::function<void(int)> to auto
make the lambda non-generic by changing auto b to int b
use Clang (e.g. v3.5.0)
I believe that this is a compiler bug related to optimisations and a failure to detect odr-use in a generic lambda (though it's interesting that setting -O0 has no effect). It could be related to bug 61814 but I don't think it's quite the same thing, therefore:
I have raised it as GCC bug 64791.
(Update: this bug has since been marked as fixed in GCC 5.0.)
Certainly I can't find anything obvious in the C++14 wording that should disallow your code, though there's very little "obvious" in general in the new C++14 wording. :(
[C++14: 5.1.2/6]: [..] For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. [..]
[C++14: 5.1.2/12]: A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:
odr-uses (3.2) the entity, or
names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
[ Example:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
};
}
—end example ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]
[C++14: 5.1.2/13]: An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used (3.2) in the scope containing the lambda-expression. [..]
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
Don't know if it should work with std::function but this works for sure.
Further investigation:
I created a class to mimic as closely as possible the lambda:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(int b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{};
f2(3); // <- this works
This suggests that your example should have worked. After all lambdas are the same in behavior with an object who has the operator() overloaded and fields for the captured variables.
If we change the class to get to the auto part:
This doesn't work:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(auto b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- doesn't work
However this works:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
template <class T>
auto operator()(T b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- this works
So most likely it is related to the use of auto as parameter of lambda/functions, a feature new to C++14 so most likely without a mature implementation.