Every now and then I come up with code like this:
// lazy lambda:
[&] {
// use references to a, b, c
}
// pedantic lambda
[&a, &b, &c] {
// use references to a, b, c
}
I'd like to know which of the lambdas is better in terms of performance and perhaps executable size according to the C++14 standard (or later) and your practical experience with compilers.
There is no difference in this example. The compiler will only capture variables that are explicitly referenced in the lambda, making the lazy capture equivalent to the explicit one.
A lazy capture is slightly more convenient. Changing the body of the lambda to use additional captured variables, or removing all references to a captured variable from an existing lambda, would normally also require updating an explicit capture list. With a lazy capture the compiler will do everything for you.
Excluding the trivial case where you explicitly capture something that isn't mentioned in the lambda at all, there are two corner cases where there might be a difference.
First, implicit captures generally do not capture entities that aren't odr-used (but see the next item for the exception to this). This includes, among other things, things mentioned in unevaluated operands such as those of decltype and sizeof, as well as certain const and constexpr local variables when used in certain contexts (consult [basic.def.odr] for the full set of rules):
void f(int){}
void f2(const int &) {}
void t() {
const int x = 8;
constexpr double y = 8;
const double z = 8;
auto g = []{ f(x); }; // OK
auto g1 = [=]{ f(x); }; // OK, does not capture x
// usually won't fire, though not guaranteed
static_assert(sizeof(g) == sizeof(g1), "!!");
auto g2 = []{ f(y); }; // OK
auto g3 = []{ f(z); }; // Error, must capture z
auto g4 = []{ f2(x); }; // Error, must capture x since it's odr-used
auto g5 = [=]{ f2(x); }; // OK, captures x
auto g6 = []{ f2(+x); }; // OK, doesn't odr-use x
auto g7 = []{ f2(y); }; // OK
}
If you write the capture list manually, it's possible that you'll capture more than you technically need to, because the rules governing what is or isn't odr-used are pretty complex.
Second, implicit captures in generic lambdas will capture things used in a dependent expression, even when they aren't necessarily odr-used, for sanity. Borrowing an example from the standard:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
// Will actually odr-use x only if sizeof(a) == 1
};
}
However, you are not actually required to capture something that may-or-may-not-be-odr-used when writing a generic lambda; you are only required to capture it if you instantiate a specialization of the function call operator that odr-uses the thing. And so if you never call the resulting generic lambda in a way that would odr-use the thing at issue, then the implicit capture may capture more than the minimum you need. For example, this is allowed:
void test() {
const int x = 17;
auto g3 = [](auto a) { // no capture
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK for now
};
}
as long as you don't call g3 with anything whose size is 1: g3(0) is OK on typical systems; g3('\0') isn't.
Like people said above, there will be no difference in regards to generated code. However, I believe, it is better to be explicit - it gives better readability (easier to reason about variable life cycle) and also gives you better support from the compiler, producing errors when you access the variable you didn't mean to.
I believe 'lazy capturing' to be not a good feature at all.
Related
[expr.prim.lambda.capture]/7:
If an expression potentially references a local entity within a scope in which it is odr-usable, and the expression would be potentially evaluated if the effect of any enclosing typeid expressions ([expr.typeid]) 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. The implicit capture of *this is deprecated when the capture-default is =; see [depr.capture.this]. [Example 4:
void f(int, const int (&)[2] = {});
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK, calls #1, does not capture x
};
auto g1 = [=](auto a) {
f(x); // OK, calls #1, captures x
};
}
... Within g1, an implementation can optimize away the capture of x as it is not odr-used. — end example]
So an entity is captured even if it is not odr-used by the lambda body, and the example below says that the implementations can optimize it away. Therefore, since the implementations can optimize it away, why add such a rule? What's the point of it?
cppreference says it was added in C++17. What's the original proposal?
P0588R0 contains the rationale explaining why the rules for implicit capture were changed in order to capture some variables that are not going to be odr-used anyway. It's very subtle.
Basically, in order to determine the size of a lambda closure type, you need to know which variables are captured and which ones are not, but under the old rules:
in order to determine whether a variable is captured implicitly, you have to know whether it's odr-used, and
in order to know whether the variable is odr-used, you have to perform substitution into the lambda's body, and
if the function call operator is generic, it means the above substitution is done at a point where the function call operator itself is not ready to be instantiated yet (since its own template parameters aren't yet known). This causes problems that could be avoided by not doing the substitution in the first place.
The paper gives the following example:
template<typename T> void f(T t) {
auto lambda = [&](auto a) {
if constexpr (Copyable<T>() && sizeof(a) == 32) {
T u = t;
} else {
// ... do not use t ...
}
};
// ...
}
When f is instantiated with a non-copyable type, ideally we would like the branch with T u = t; to be discarded. That's what the if constexpr is there for. But an if constexpr statement does not discard the un-taken branch until the point at which the condition is no longer dependent---meaning that the type of a must be known before T u = t; can be discarded. Unfortunately, under the old rules, T must be substituted into T u = t; in order to determine whether t is captured, and this happens before any opportunity to discard this statement
The new rules simply declare that t is captured; therefore, the compiler doesn't have to perform any substitution into the lambda body until the point at which a specialization of the function call operator is referenced (and thus, its body can be fully instantiated). And if the compiler is somehow able to prove that t will never be odr-used by any possible specialization of the function call operator, it's free to optimize it out.
For example:
void foo()
{
if constexpr (...)
int x = 5;
else
double x = 10.0;
bar(x); // calls different overloads of bar with different values
}
It's common case in D lang, but I didn't found info about C++17.
Of course, it is possible to use something like
std::conditional<..., int, double>::type x;
but only in elementary cases. Even different initializators (as above) creates big problem.
There is no way this code could work. The problem is that x is out of scope when you are calling bar. But there is a workaround:
constexpr auto t = []() -> auto {
if constexpr(/* condition */) return 1;
else return 2.9;
}();
bar(t);
To explain a little bit, it uses instantly invoked lambda expression along with auto return type deduction. Therefore we are giving t value in place and it does not get out of the scope.
Of course, it wouldn't work if the if statement couldn't have been evaluated at compile time. And if you want to do some runtime operations inside of this lambda you cannot have t as constexpr, but it will still work.
There are two ways this won't work.
First, a variable is limited to the scope in which it's declared. Leaving out the braces isn't gonna fool the compiler: int x = 5 is still in its own scope, and disappears immediately after it appears.
Second, the relaxed grammar rules for if constexpr only apply within the if constexpr's body. It would be infeasible to allow the context created in the body to leak out to the surrounding scope, since by definition it may not be well-formed, or consistent between the then/else block. (What if the else-block declared x as a typename?)
Bottom line, you'll need to move bar() into the if-body, or template foo() itself and have the type and value of x determined by your ....
From declarations / functions / 9.3.4.6 / 6.2 (i apologize on how to cite the specific sentence from standard):
An explicit-object-parameter-declaration is a parameter-declaration with a this specifier. An explicit-object-parameter-declaration shall appear only as the first parameter-declaration of a parameter-declaration-list of either: (6.1) a member-declarator that declares a member function ([class.mem]), or (6.2) a lambda-declarator ([expr.prim.lambda]).
If this as explicit object parameter is permitted from lambda expressions, what will happen when we capture variables at the same time?
Based on my understandings, if we have lambda under the hood:
[x = 2](this auto& func) { x = 4; }();
may have the rough equivalent of:
class lambda01 {
private:
int x;
public:
constexpr lambda01(cons int& x_)
: x{x_} {}
constexpr void operator()(this lambda01& func) {
func.x = 4;
}
};
lambda04 lambda04_obj {2};
lambda04_obj.operator()();
if it's right.
For example no. 1:
int x;
// is it:
[&x](this auto& func){ x = 4; }();
assert(x == 4);
// or:
[&x](this auto& func){ func.x = 2; }();
assert(x == 2);
Are both expressions valid?
Is lambda taking l-value object parameter valid?
For example no. 2 that will print arguments in variadic:
[]<typename... Args>(const Args&... args) {
[&](this auto func){
/** ... **/
};
}(1, 2, 3, 4);
From the commented expression, which one is valid?
(std::cout << args << '\n', ...)
(std::cout << func.args << '\n', ...)
both
neither
If the second choice is valid, then that would deserve another question regarding the possible parameter packs in 1 object.
In short, is it valid to use captured variables accessed with dot operator in lambda taking explicit object parameter?
The standard doesn't allow it:
For each entity captured by copy, an unnamed non-static data member is declared in the closure type.
If it's "unnamed", then you can't name it. There's specific language that causes the name of a captured entity to be transformed into a this-based expression, but that's it.
So you can take an explicit this parameter, and names of captured entities will automatically use that. But you can't access those variables through the explicit parameter.
The only reason to explicitly take this in a lambda is to use the interfaces the standard provides: calling the lambda. AKA: recursively calling the lambda without naming the lambda.
What is the point of defining a local variable to be an rvalue reference or a forwarding (universal) reference? As far as I understand, any variable that has a name is an lvalue and will be treated as such moving forward.
Example:
Widget&& w1 = getWidget();
auto&& w2 = getWidget();
w1 and w2 are both lvalues, and will be treated as such if they're passed as arguments later on. Their decltype is probably not, but what difference does this make? Why would anyone need to define variables that way?
If you have a function returning a temporary which cannot be moved.
Foo some_function();
auto&& f = some_function();
This is legal. auto f = some_function(); will either copy (which could be expensive), or fail to compile (if the class also cannot be copied).
In general, auto&& deduces down to either an r or lvalue reference depending on what it is initialized with, and if initialized with a temporary extends its lifetime while giving you access to it as an lvalue.
A classic use is in the 'just-loop' pattern:
for( auto&& x : some_range )
where there is actually a auto&& x = *it; in the code generated.
You cannot bind a non-constant lvalue reference to a temporary, so your other choice is Widget const&, which doesn't let you modify the temporary during its lifetime.
This technique is also useful to decompose a complex expression and see what is going on. So long as you aren't working with extremely fragile expression templates, you can take the expression a+b+c*d and turn it into
auto&& c_times_d = d*d;
auto&& b_plus_c_times_d = b + decltype(c_times_d)c_times_d;
auto&& c_plus_b_plus_c_times_d = c + decltype(b_plus_c_times_d)b_plus_c_times_d;
and now you have access to the temporary objects whose lifetime is extended, and you can easily step through the code, or introduce extra steps between steps in a complex expression: and this happens mechanically.
The concern about fragile expression templates only holds if you fail to bind every sub-expression. (Note that using -> can generate a myriad of sub-expressions you might not notice.)
I use auto&& when I want to say "I'm storing the return value of some function, as is, without making a copy", and the type of the expression is not important. auto is when I want to make a local copy.
In generic code it can be highly useful.
Foo const& a(Foo*);
Bar a(Bar*);
template<class T>
auto do_stuff( T*ptr ) {
auto&& x = a(ptr);
}
here if you pass a Bar* it stores the temporary, but if you pass a Foo* to do_stuff it stores the const&.
It does the least it can.
Here is an example of a function returning a non-movable non-copyable object, and how auto&& lets you store it. It is otherwise useless, but it shows how it works:
struct Foo {
Foo(&&)=delete;
Foo(int x) { std::cout << "Foo " << x << " constructed\n";
};
Foo test() {
return {3};
}
int main() {
auto&& f = test();
}
As far as I know, there is no real, aka widely used purpose to define a local rvalue reference, since their nature, to not to bind to lvalues, is only helpful in overloading and deduction, so to define them as parameters of a function.
One could use them, to bind them to temporary values like
int &&rref = 5*2;
but since almost all compilers are optimizing the expression
int i = 5*2;
there is no real need, in therm of performance or avoid copying.
One example could be an array
template<class T, int N> using raw_array = T[N];
then
auto && nums = raw_array<int,4>{101, 102, 103, 104};
this allows the temporary to be used as if it was an ordinary array.
A variable declared auto&& will follow perfect forwarding rules. Try it for yourself. This is even how perfect forwarding is done with lambdas in c++14.
const Type& fun1();
Type&& fun2();
auto&& t1 = fun1(); // works
auto&& t2 = fun2(); // works too
I'm building a lambda function that requires access to a fair number of variables in the context.
const double defaultAmount = [&]{
/*ToDo*/
}();
I'd rather not use [=] in the list as I don't want lots of value copies to be made.
I'm concerned about program stability if I use [&] since I don't want the lambda to modify the capture set.
Can I pass by const reference? [const &] doesn't work.
Perhaps a good compiler optimises out value copies, so [=] is preferable.
You can create and capture const references explicitly:
int x = 42;
const int& rx = x;
auto l = [&rx]() {
x = 5; // error: 'x' is not captured
rx = 5; // error: assignment of read-only reference 'rx'
};
The capture list is limited in what can be captured; basically by-value or by-reference (named or by default), the this pointer and nothing.
From the cppreference;
capture-list - a comma-separated list of zero or more captures, optionally beginning with a capture-default. Capture list can be passed as follows (see below for the detailed description):
[a,&b] where a is captured by value and b is captured by reference.
[this] captures the this pointer by value
[&] captures all automatic variables odr-used in the body of the lambda by reference
[=] captures all automatic variables odr-used in the body of the lambda by value
[] captures nothing
You could create local const& to all the object you wish to capture and use those in the lambda.
#include <iostream>
using namespace std;
int main()
{
int a = 5;
const int& refa = a;
const int b = [&]() -> int {
//refa = 10; // attempts to modify this fail
return refa;
}();
cout << a << " " << b << endl;
}
The capture could be either for all the references, or an explicit list what is required;
const int b = [&refa]()
Another alternative is not to capture the local variables at all. You then create a lambda that accepts as arguments the variables you need. It may be more effort as the local variable count grows, but you have more control over how the lambda accepts its arguments and is able to use the data.
auto lambda = [](const int& refa /*, ...*/) { */...*/ }
lambda(...);
Sadly the C++11 grammar does not allow for this, so no.
You can capture a constant reference to an object, not an object itself:
A a;
const A& ref_a = a;
const double defaultAmount = [&]{
ref_a.smth();
}();