Accessing captured variables through explicit this parameter in lambda - c++

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.

Related

Forbidden syntax for pointer/reference to bound member function

Suppose I have the following:
struct A {
int foo(int bar) const { return bar; }
};
and I want to specify a name that refers to a "bound" member function (i.e.):
A a;
auto opt1 = a.foo; // Forbidden, instead do something like...
auto opt2 = [&a] (int i) { return a.foo(i); }; // or ...
auto opt3 = std::bind(&A::foo, a, std::placeholders::_1);
it is then simple to invoke the bound member function:
assert(opt1(42) == 42); // If 'opt1' were allowed
assert(opt2(42) == 42);
assert(opt3(42) == 42);
In my view, opt1 would be the preferred solution to achieve the goal. However, specifying a bound function via opt1 is forbidden by the language.
My question is purely legal: What part of the C++(20) standard forbids a construct like opt1? My question is not why, but where.
[expr.ref]:
[for the expression E1.E2]....if E1.E2 refers to a non-static member function...The expression can be used only as the left-hand operand
of a member function call.

Examples of mandatory type deduction in C++

Google's C++ Style Guide at some point states:
There are several contexts in which C++ allows (or even requires) types to be deduced by the compiler.
What are some examples of mandatory type deduction?
A simple example is initialisation of a lambda variable. The type of the lambda is anonymous, therefore it cannot be named explicitly and must be deduced:
auto var = [capture]{};
Another example:
struct {
int x, y;
} g_xy;
Here g_xy is a global variable of unnamed type. If you try to return it for example you have to let the compiler deduce the return type of the function because you cannot name it:
auto foo()
{
return g_xy;
}
Although possible unnamed types (except lamdas) are rarely useful and used.
The style guide gives several examples where the compiler does automatic type deduction. The most obvious case is whenever you use the auto keyword, introduced with C++11. auto is a placeholder for an actual type. Whenever you use auto the compiler will deduce the type from: the type of expression used to intialise a variable; the trailing type or type of return expression of a function.
Normally you would declare a variable like this:
int i = 0;
where you specify the type int for the variable i. However, in modern C++ you could declare variables without specifying their type and the compiler will deduce their types automatically:
auto a = 42; // a is an int
auto& b = a; // b is an int&
auto c = b; // c is an int
auto d{42}; // d is an int, not a std::initializer_list<int>
auto v = {1, 2, 3}; //v is a std::initializer_list<int>
Other examples include a named lambda function:
auto lower = [] (const char c) { return tolower(c); };
and in C++14 onward, a generic lambda where both the return type and lambda parameters can be auto:
auto add = [](const auto a, const auto b) { return a + b; }
One thing to note is that auto is a placeholder for type, not for const, volatile or reference specifers.
Some advantages of using auto include:
variable is always initialised
ensure the correct type is used without any implicit conversion
less typing and concern for actual type
And yet another example - type of generic lambdas with auto arguments can not be specified.
auto lam = [](auto v) { }
There is no way to specify type of v in invocation of lambda.

How to be explicit about the type of a captured variable in a lambda?

How can I explicitly specify the type of a captured variable for a lambda in C++?
For example, assume I have a function that takes a universal reference and I want to perfect-forward it into a lambda.
I found out, that I can use a std::tuple for this as shown below, but I wonder if there is a more succinct way.
template<typename T>
auto returns_functor (T&& value)
{
return [value = std::tuple<T> (std::forward<T> (value))] ()
{
/* use std::get<0> (value) */
};
}
Related: Capturing perfectly-forwarded variable in lambda (the accepted answer there suggests this is a different question, but further answers give essentially the above solution).
Another way of capturing rvalue ( universal reference) is as follows,
template< typename T>
T getRvalue( T&& temp)
{
auto lambda = [ rTemp = std::move( temp)]() mutable
{
return T( std::forward< T>( rTemp));
};
return lambda();
}
std::move() allows to capture objects of movable type those can't be captured by copy.
mutable is very important here in lambda, until mutable is not used, objects captured by lambda can't be modified and compile will generate following error,
cannot conver 'rTemp (type 'const T') to type 'std::remove_reference<T>::type&' {aka 'T&'}
return T( std::forward< T>( rTemp));
Now about first question,
"How can I explicitly specify the type of a captured variable for a lambda in C++?" So It is not required to specify the type, compile will do it, see following link,
https://en.cppreference.com/w/cpp/language/lambda
It states A capture with an initializer acts as if it declares and explicitly captures a variable declared with type auto, whose declarative region is the body of the lambda expression. In simple words, it means auto variable get deduced according to initializer and doesn't requires to specify it's type explicitly, for example
int x = 1;
auto lambda = [ y = x + 2](){ std::cout<< "y = "<< y<< std::endl;};
Here y would be deduced as int by initializer x + 2.

How to define a lambda expression without both std::function and auto?

I have read the item31 of "Effective Modern C++" and web page of http://en.cppreference.com/w/cpp/language/lambda and wonder if I can define a lambda by its definite type instead of the wrapped type of std::function or keyword of auto and how can I accomplish that.
for instance, for the type int:
auto x_1 = 5; // type deduction
int x_2 = 5; // defined by definite type
// both x_1, x_2 are int variables of value 5
now, when the problem comes to the lambda:
auto f_1_0 = []()->int{return 5;};
std::function<int(void)> f_1_1 = []()->int{return 5;};
SomeType f_2 = []()->int{return 5;}; // what's the SomeType here?
Each lambda expression has its own unique type.
Here the expressions f_1 and f2 have different types.
auto f_1 = []()->int {return 5; };
auto f_2 = []()->int {return 5; };
Assigning f_2 = f_1 is illegal.
The standard says the types are "unnamed." In practice, the compiler probably makes up a new, hiiden typename for each lambda. Visual C++17 gave them the following names.
classmain::<lambda_7e9d7fb093569d78a8c871761cbb39d7>
classmain::<lambda_8f061a3967cd210147d6a4978ab6e125>
Not very useful information.
The standard says that the type of the lambda is unnamed so the implementation creates a implementation defined name it uses similar to the other unnamed classes, structs enumc etc.
ISO C++: 5.1.2 Lambda expressions [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.
The standard also says that the lambda 'behaves like a function' so you it could be used with the std::function template:
[Note: A closure object behaves like a function object (20.9).—end note]
And if you really want to have names for your can use the old-fashioned functors and do all the work the compiler do for you with the lambdas.
in some cases you can use function pointers (using mvcpp, 0x17):
auto usingAuto = []() {
cout << "autoMagick" << endl;
};
void(*pureCpp)() = []() {
cout << "pureCpp" << endl;
};
//pureCpp = usingAuto; //you can even assing function pointers from lambdas
//usingAuto = pureCpp; //error
pureCpp();
usingAuto();

Why does a lambda have a size of 1 byte?

I am working with the memory of some lambdas in C++, but I am a bit puzzled by their size.
Here is my test code:
#include <iostream>
#include <string>
int main()
{
auto f = [](){ return 17; };
std::cout << f() << std::endl;
std::cout << &f << std::endl;
std::cout << sizeof(f) << std::endl;
}
The ouptut is:
17
0x7d90ba8f626f
1
This suggests that the size of my lambda is 1.
How is this possible?
Shouldn't the lambda be, at minimum, a pointer to its implementation?
The lambda in question actually has no state.
Examine:
struct lambda {
auto operator()() const { return 17; }
};
And if we had lambda f;, it is an empty class. Not only is the above lambda functionally similar to your lambda, it is (basically) how your lambda is implemented! (It also needs an implicit cast to function pointer operator, and the name lambda is going to be replaced with some compiler-generated pseudo-guid)
In C++, objects are not pointers. They are actual things. They only use up the space required to store the data in them. A pointer to an object can be larger than an object.
While you might think of that lambda as a pointer to a function, it isn't. You cannot reassign the auto f = [](){ return 17; }; to a different function or lambda!
auto f = [](){ return 17; };
f = [](){ return -42; };
the above is illegal. There is no room in f to store which function is going to be called -- that information is stored in the type of f, not in the value of f!
If you did this:
int(*f)() = [](){ return 17; };
or this:
std::function<int()> f = [](){ return 17; };
you are no longer storing the lambda directly. In both of these cases, f = [](){ return -42; } is legal -- so in these cases, we are storing which function we are invoking in the value of f. And sizeof(f) is no longer 1, but rather sizeof(int(*)()) or larger (basically, be pointer sized or larger, as you expect. std::function has a min size implied by the standard (they have to be able to store "inside themselves" callables up to a certain size) which is at least as large as a function pointer in practice).
In the int(*f)() case, you are storing a function pointer to a function that behaves as-if you called that lambda. This only works for stateless lambdas (ones with an empty [] capture list).
In the std::function<int()> f case, you are creating a type-erasure class std::function<int()> instance that (in this case) uses placement new to store a copy of the size-1 lambda in an internal buffer (and, if a larger lambda was passed in (with more state), would use heap allocation).
As a guess, something like these is probably what you think is going on. That a lambda is an object whose type is described by its signature. In C++, it was decided to make lambdas zero cost abstractions over the manual function object implementation. This lets you pass a lambda into a std algorithm (or similar) and have its contents be fully visible to the compiler when it instantiates the algorithm template. If a lambda had a type like std::function<void(int)>, its contents would not be fully visible, and a hand-crafted function object might be faster.
The goal of C++ standardization is high level programming with zero overhead over hand-crafted C code.
Now that you understand that your f is in fact stateless, there should be another question in your head: the lambda has no state. Why does it not size have 0?
There is the short answer.
All objects in C++ must have a minimium size of 1 under the standard, and two objects of the same type cannot have the same address. These are connected, because an array of type T will have the elements placed sizeof(T) apart.
Now, as it has no state, sometimes it can take up no space. This cannot happen when it is "alone", but in some contexts it can happen. std::tuple and similar library code exploits this fact. Here is how it works:
As a lambda is equivalent to a class with operator() overloaded, stateless lambdas (with a [] capture list) are all empty classes. They have sizeof of 1. In fact, if you inherit from them (which is allowed!), they will take up no space so long as it doesn't cause a same-type address collision. (This is known as the empty base optimization).
template<class T>
struct toy:T {
toy(toy const&)=default;
toy(toy &&)=default;
toy(T const&t):T(t) {}
toy(T &&t):T(std::move(t)) {}
int state = 0;
};
template<class Lambda>
toy<Lambda> make_toy( Lambda const& l ) { return {l}; }
the sizeof(make_toy( []{std::cout << "hello world!\n"; } )) is sizeof(int) (well, the above is illegal because you cannot create a lambda in a non-evaluated context: you have to create a named auto toy = make_toy(blah); then do sizeof(blah), but that is just noise). sizeof([]{std::cout << "hello world!\n"; }) is still 1 (similar qualifications).
If we create another toy type:
template<class T>
struct toy2:T {
toy2(toy2 const&)=default;
toy2(T const&t):T(t), t2(t) {}
T t2;
};
template<class Lambda>
toy2<Lambda> make_toy2( Lambda const& l ) { return {l}; }
this has two copies of the lambda. As they cannot share the same address, sizeof(toy2(some_lambda)) is 2!
A lambda is not a function pointer.
A lambda is an instance of a class. Your code is approximately equivalent to:
class f_lambda {
public:
auto operator() { return 17; }
};
f_lambda f;
std::cout << f() << std::endl;
std::cout << &f << std::endl;
std::cout << sizeof(f) << std::endl;
The internal class that represents a lambda has no class members, hence its sizeof() is 1 (it cannot be 0, for reasons adequately stated elsewhere).
If your lambda were to capture some variables, they'll be equivalent to class members, and your sizeof() will indicate accordingly.
Your compiler more or less translates the lambda to the following struct type:
struct _SomeInternalName {
int operator()() { return 17; }
};
int main()
{
_SomeInternalName f;
std::cout << f() << std::endl;
}
Since that struct has no non-static members, it has the same size as an empty struct, which is 1.
That changes as soon as you add a non-empty capture list to your lambda:
int i = 42;
auto f = [i]() { return i; };
Which will translate to
struct _SomeInternalName {
int i;
_SomeInternalName(int outer_i) : i(outer_i) {}
int operator()() { return i; }
};
int main()
{
int i = 42;
_SomeInternalName f(i);
std::cout << f() << std::endl;
}
Since the generated struct now needs to store a non-static int member for the capture, its size will grow to sizeof(int). The size will keep growing as you capture more stuff.
(Please take the struct analogy with a grain of salt. While it's a nice way to reason about how lambdas work internally, this is not a literal translation of what the compiler will do)
Shouldn't the lambda be, at mimumum, a pointer to its implementation?
Not necessarily. According to the standard, the size of the unique, unnamed class is implementation-defined. Excerpt from [expr.prim.lambda], C++14 (emphasis mine):
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.
[ ... ]
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
— the size and/or alignment of the closure type,
— whether the closure type is trivially copyable (Clause 9),
— whether the closure type is a standard-layout class (Clause 9), or
— whether the closure type is a POD class (Clause 9)
In your case -- for the compiler you use -- you get a size of 1, which doesn't mean it's fixed. It can vary between different compiler implementations.
From http://en.cppreference.com/w/cpp/language/lambda:
The lambda expression constructs an unnamed prvalue temporary object of unique unnamed non-union non-aggregate class type, known as closure type, which is declared (for the purposes of ADL) in the smallest block scope, class scope, or namespace scope that contains the lambda expression.
If the lambda-expression captures anything by copy (either implicitly with capture clause [=] or explicitly with a capture that does not include the character &, e.g. [a, b, c]), the closure type includes unnamed non-static data members, declared in unspecified order, that hold copies of all entities that were so captured.
For the entities that are captured by reference (with the default capture [&] or when using the character &, e.g. [&a, &b, &c]), it is unspecified if additional data members are declared in the closure type
From http://en.cppreference.com/w/cpp/language/sizeof
When applied to an empty class type, always returns 1.