Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

About lambdas, conversions to function pointers and visibility of private data members

Consider the following example:

#include <cassert>

struct S {
    auto func() { return +[](S &s) { s.x++; }; }
    int get() { return x; }

private:
    int x{0};
};

int main() {
    S s;
    s.func()(s);
    assert(s.get() == 1);
}

It compiles both with G++ and clang, so I'm tempted to expect that is allowed by the standard. However, the lambda has no capture list and it cannot have it because of the + that forces the conversion to a function pointer. Therefore, I expected it was not allowed to access private data members of S.
Instead, it behaves more or less how if it was defined as a static member function.

So far, so good. If I knew it before, I would have used this trick often to avoid writing redundant code.

What I'd like to know now is where in the standard (the working draft is fine) this is defined, for I've not been able to find the section, the bullet or whatever that rules about it.
Is there any limitation for the lambda or it works exactly as if it was defined as a static member function?

like image 814
skypjack Avatar asked Feb 05 '18 16:02

skypjack


2 Answers

For lambda expressions inside the member function, according to §8.4.5.1/2 Closure types [expr.prim.lambda.closure]:

The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.

That means the lambda closure type will be declared inside the member function, i.e. a local class. And according to §14/2 Member access control [class.access]:

(emphasis mine)

A member of a class can also access all the names to which the class has access. A local class of a member function may access the same names that the member function itself may access.

That means for the lambda expression itself, it could access the private members of S, same as the member function func.

And §8.4.5.1/7 Closure types [expr.prim.lambda.closure]:

(emphasis mine)

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 having the same parameter and return types as the closure type's function call operator. ... The value returned by this conversion function is the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator.

That means when the converted function pointer gets invoked the same rule applies.

like image 165
songyuanyao Avatar answered Nov 14 '22 23:11

songyuanyao


However, the lambda has no capture list and it cannot have it because of the + that forces the conversion to a function pointer.

+ does not force a conversion to a function pointer, but adds a conversion operator to pointer to function for you to use as an option. Lambda remains a lambda, with all the access privileges granted to it, i.e. it may access the same names that the member function itself may access.

like image 6
Sergey Kalinichenko Avatar answered Nov 14 '22 22:11

Sergey Kalinichenko