Using of not captured variable in lambda - c++

I can not quite understand an example from C++14 standard draft N4140 5.1.2.12 [expr.prim.lambda].
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 initcapture’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 the entity, or
names the entity in a potentially-evaluated expression 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 ]
I thought that the beginning of a phrase a lambda-expression with an associated capture-default should prohibit any implicit capture (and it's confirmed by comment), therefore #1 call will lead to an error (something about using not captured variable). So how it works? What will be first argument of f? What if g will be called after exiting test() scope? What if I change #1 signature to void(const int&)?
--
upd: Thanks to all for explanation of how it works. Later I'll try to find and post references to standard about this case.

As T.C. said in his comment, #1 does not require a capture as x is known at compile time and is therefore baked into the lambda. Not unlike how the function f is known at compile time so it doesn't need to be captured.
I believe if you change f's signature to int const & you are now attempting to pass the address of the constant which is on the stack, thus subject to changes, and it would require capturing x by value or reference.

Related

From what part of the standard should I understand I can use a constexpr local variable in a if constexpr condition in a lambda without capturing it?

This compiles
void f() {
constexpr bool x{};
auto g = []{
if constexpr (x) {
}
};
g();
}
int main () {
f();
}
and it compiles even if I change [] to [x] or to [&x]. In these two latter cases, though, clangd tells me
Lambda capture 'x' is not required to be captured for this use (fix available) [-Wunused-lambda-capture]
A first minor question would be: is capturing x in this case simply redundant? Or there's more?
But my main question is: from where in the standard should I understand that I can make without any capture in the case above? After all, just writing (void)x; before the if constexpr, for instance, makes the capture ([x] or [&x]) necessary.
A variable is required to be captured if it is a local entity that is odr-used within the lambda expression. See [basic.def.odr]/10. In your example, x is a local entity but it is not odr-used by if constexpr (x) because:
x is a variable of non-reference type that is usable in constant expressions and has no mutable subobjects, and
the lvalue-to-rvalue conversion is applied immediately.
See [basic.def.odr]/5.2. (In a limited set of situations, the lvalue-to-rvalue conversion is allowed to not be done immediately; see [basic.def.odr]/3.)
In general, [basic.def.odr]/5 is the place to look when you need to determine whether a variable is odr-used.
The case is ODR-usage (or rather: non-ODR-usage in this particular case) and it's not C++20-specific.
See expr.prim.lambda.capture
If an expression potentially references a local entity within a declarative region in which it is odr-usable, and
the expression would be potentially evaluated if the effect of any enclosing typeid expressions (7.6.1.7) were
ignored, the entity is said to be implicitly captured by each intervening lambda-expression with an associated
capture-default that does not explicitly capture it.
Taking a look into the examples and notes that follow may also be worthwhile:
Note: The set of captured entities is determined syntactically, and entities might be implicitly captured even
if the expression denoting a local entity is within a discarded statement (8.5.1). Example:
template<bool B>
void f(int n) {
[=](auto a) {
if constexpr (B && sizeof(a) > 4) {
(void)n; // captures n regardless of the value of B and sizeof(int)
}
}(0);
}
In previous standards it was more clearly stated imho, see: https://stackoverflow.com/a/42611583/4885321

comma operator makes lambda expression non-constexpr

According to [this Q&A] since c++11 comma operator is constexpr capable. According to [this Q&A] constexpr variable should not be captured by lambda but should be usable inside its body.
Both these rules make following code compilable in clang:
//Example 1
template <int>
struct Foo {};
int main() {
constexpr int c = 1;
static_cast<void>(Foo<(c, 2)>{});
}
//Example 2
template <int>
struct Foo {};
int main() {
constexpr int c = 1;
auto lambda = []{return c * 2;};
static_cast<void>(Foo<lambda()>{});
}
However while both these examples compile successfully on clang (that declares constexpr lambda support that is -- 8.0.0) the following snippet doesn't and I can't imagine why... Any ideas?
template <int>
struct Foo {};
int main() {
constexpr int c = 1;
auto lambda = []{return (c, 2);};
static_cast<void>(Foo<lambda()>{});
}
Compilation error:
variable 'c' cannot be implicitly captured in a lambda with no capture-default specified
[live demo]
Its seems to be a clang bug, according to [basic.def.odr]/4:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying the lvalue-to-rvalue conversion (7.1) to x yields a constant expression (8.20) that does not invoke any non-trivial functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (7.1) is applied to e, or e is a discarded-value expression.
As has been commented, the issue is not limited to the comma operator but for every discarded expressions, these expression doesn't constitute odr-use, hence it must be accepted.
This is a clang bug, if we look at a simpler case:
constexpr int c = 1;
auto lambda = [] {return c,2;};
clang also considers this ill-formed (see it live), the lambda is required to capture an automatic variable if it odr-uses it see expr.prim.lambda.capturep8:
An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used in the scope containing the lambda-expression. If *this is captured by a local lambda expression, its nearest enclosing function shall be a non-static member function. 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. If a lambda-expression captures an entity and that entity is not defined or captured in the immediately enclosing lambda expression or function, the program is ill-formed. ...
and discarded value expression is not an odr-use.
I found a similar bug report [rejects valid] constexpr non-scalar variable not usable in lambda without capture or local class.

Why does [=]{} have a lambda capture?

At an intuitive level, it makes sense that a lambda that needs to carry no state (through a reference or otherwise) should be cleanly convertible to a naked function pointer. However, I was recently surprised to see the following failing in GCC, Clang, and MSVC:
int main(int, char *[]) {
void (*fp)() = []{}; // OK
//fp = [=]{}; // XXX - no user defined conversion operator available
//fp = [&]{}; // XXX - same ...
}
The C++17 spec (or at least visible public draft version N4713), refers in item 7 of § 8.4.5.1 [expr.prim.lambda.closure] to lambdas with and without captures:
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 (10.5) having the same parameter and return types as the closure type’s function call operator. ...
However, looking into the formal grammar you can see the following in § 8.4.5 [expr.prim.lambda]:
lambda-expression :
lambda-introducer compound-statement
...
lambda-introducer :
[ lambda-captureopt ]
...
and in § 8.4.5.2 [expr.prim.lambda.capture]:
lambda-capture :
capture-default
capture-list
capture-default, capture-list
capture-default :
&
=
So all the compilers were actually obeying the letter of the law to my dismay...
Why does the language define the existence of a capture as a narrow grammatical distinction in the declaration instead of basing it on whether the body contains references to any non-static/captured state?
The change that allowed the conversion was initiated by a national body comment. See n3052: Converting Lambdas to Function Pointers which refers to national body comment UK 42:
A lambda with an empty capture list has identical semantics to a regular function type. By requiring this mapping we get an efficient lambda type with a known API that is also compatible with existing operating system and C library functions.
and the resolution from N3052 was:
Resolution: Add a new paragraph: "A lambda expression with an empty capture set shall be convertible to pointer to function type R(P), where R is the return type and P is the parameter-type-list of the lambda expression." Additionally it might be good to (a) allow conversion to function reference and (b) allow extern "C" function pointer types.
...
Add a new paragraph after paragraph 5. The intent of this edit is to obtain a closure-to-function-pointer conversion for a lambda with no lambda-capture.
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type's function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type's function call operator.
and that is where we are today. Note the comment said empty capture list and what we have today seems to match the intent as worded in the comment.
It looks like it was a fix based on a national body comment and was applied narrowly.
The rule you propose would be extremely fragile, especially in the pre-P0588R1 world when implicit captures depended on odr-use.
Consider:
void g(int);
void h(const int&);
int my_min(int, int);
void f(int i) {
const int x = 1, y = i;
[=]{ g(x); }; // no capture, can convert?
[=]{ g(y); }; // captures y
[=]{ h(x); }; // captures x
[=]{ my_min(x, 0); }; // no capture, can convert?
[=]{ std::min(x, 0); }; // captures x
}

how is this lambda with an empty capture list able to refer to reaching-scope name?

In the C++14 standard § 5.1.2/12 it shows an example of a lambda expression that apparently seems to be able to refer to a reaching scope's variable x, even though:
the capture list is empty, i.e. no capture-default
the comment says that it "does not capture x"
Here's the example:
void f(int, const int (&)[2] = {}) { } // #1
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
}
See that it does compile. It seems to hinge on x being const; if the const is removed, it no longer compiles for the reasons one would expect (capture list is empty). It happens even if I make the parameter be int so that it's no longer a generic lambda.
How is it possible for the lambda to refer to x even though the capture list is empty? And how is this possible while at the same time apparently not capturing x (as the comment says)?
The closest thing I found on this subject was someone else tangentially noticing this in a comment.
Here's the full section 5.1.2/12 from the standard:
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 ]
You have the right quote. A variable needs to be captured if it is odr-used. ODR-use means basically that the variable is used in a context where it needs a definition. So either its address is taken, or a reference is taken to it, etc. One key exception is, from [basic.def.odr]:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any nontrivial
functions and, if x is an object, ex is an element of the set of potential results of an expression e, where either the lvalue-to-rvalue conversion (4.1) is applied to e, or e is a discarded-value expression (Clause
5).
So in your example, applying lvalue-to-rvalue conversion on x yields a constant expression (since x is a constant integral), so it's not odr-used. Since it's not odr-used, it doesn't have to be captured.
On the other hand, if x were bound to a reference (e.g. f took its argument as const int&), then it would be odr-used, and so would have to be captured. In the second example presented, x's "odr-use-ness" is dependent on what the generic lambda argument is, so that is considered captured anyway for sanity's sake.

Can a lambda safely return the address of a copied variable?

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 ;)