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

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?

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.

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.

Related

Instantiating a function parameter that is of private type

Given a class like this:
class MyClass {
struct PrivateType {};
public:
static void func(PrivateType) {}
};
I can't do this because PrivateType is private:
MyClass::func(MyClass::PrivateType());
But I can do this:
MyClass::func({});
So I'm instantiating a private type, what looks like, outside of the class scope. Is that an implicit conversion from an initializer list? If so, when a parameter is implicitly converted, does the conversion take place in scope of the class i.e. as if it was part of the body of the function? Why is that and is that always the case?
{} is a syntax called braced-init-list. It doesn't have a type or value category and doesn't represent any object or value like an expression does. It therefore also doesn't really make sense to consider something like a conversion from a {} to exist.
There are specific rules separate from those for expressions as arguments on how overload resolution considers _braced-init-list_s as arguments.
There are also rules separate from those for initialization from parentheses when initializing from a braced-init-list. However, after overload resolution, the function parameter is always initialized simply as if the argument was the initializer for the parameter in a copy-initialization, meaning that the PrivateType parameter object, which I will name here private_type, will be initialized as if by
PrivateType private_type = {};
in a local variable definition. However, this initialization always happens in the context of the caller. All of this is true for expressions as function arguments as well.
The initialization with the type as defined in your example would resolve to aggregate initialization, because PrivateType is an aggregate class. Since there are no members or base classes to initialize, the initialization then doesn't do anything.
None of this considers whether PrivateType is accessible (private) from the context of the caller, because accessibility only applies when deciding whether a member can be named (where e.g. overload resolution to a member function, constructor or operator overload counts as named). You are never using the name PrivateType in the caller though. private does not prevent construction of a member class in any context.

Is there no such thing as "implicit this parameter" in the Standard?

Recently, I asked this question where one of the answers says:
There's no such thing as "implicit this parameter" in the standard. The standard calls it an "implicit object parameter".
Then someone commented that:
There's no such thing as "implicit this parameter" in the standard." seems wrong. From expr.call#4: "If the function is a non-static member function, the this parameter of the function shall be initialized with a pointer to the object of the call, converted as if by an explicit type conversion."
Seeing the above comment i think that the answer is technically incorrect because the answer said that "There's no such thing as "implicit this parameter" in the standard." while the standard clearly talks about the this parameter.
So how to interpret this further (pun intended)? I mean, it seems that the standard makes a distinction between the non-static member function and a constructor in the context of this parameter. For example, the standard says that for a non-static member function, the this parameter of the function shall be initialized with a pointer to the object of the call converted as if by an explicit type conversion. But the standard doesn't say the same for constructors. So why does the standard makes this distinction? I mean why doesn't the standard says that constructors also have an this parameter that get initialized by the passed argument just like for non-static member functions. This again leads to the deeper question that if there is no this parameter in the constructor unlike non-static member function, then how are we able to use this inside the constructor. For example, we know that we can write this->p = 0 inside the constructor as well as inside a non-static member function, where p is a data member. But in case of non-static member function, this is a parameter of that particular member function so this->p makes sense. But in case of constructor this is not a parameter, so how are we able to use this->p inside the constructor.
Originally, by reading the answers here, I thought that the implicit this parameter is an implementation detail. But after reading expr.call#4 it seems that it is not an implementation detail.
If you think this is some sort of implicit parameter, type in this code:
#include <iostream>
struct SimpleThing {
int xyzzy;
SimpleThing(): xyzzy(42) {}
void print(int plugh, const int twisty) {
std::cout << xyzzy << '\n';
std::cout << plugh << '\n';
std::cout << twisty << '\n';
xyzzy = 0;
plugh = 0;
twisty = 0;
this = 0;
}
};
int main() {
SimpleThing thing;
thing.print(7, 99);
}
Then examine the errors you get:
prog.cpp: In member function ‘void SimpleThing::print(int, int)’:
prog.cpp:12:16: error: assignment of read-only parameter ‘twisty’
12 | twisty = 0;
| ~~~~~~~^~~
prog.cpp:13:16: error: lvalue required as left operand of assignment
13 | this = 0;
| ^
Note that the first two assignments work because they are modifiable variables. The third fails because it is, of course, (non-modifiable) const.
The attempted assignment to this doesn't look like any sort of "can't write to some sort of variable" diagnostic because it actually isn't.
The this keyword is a special marker inside non-static member functions (and constructors/destructors) that is translated into the address of the object being worked upon. While it may be passed as a hidden parameter, that is very much an implementation detail with which the standard does not concern itself.
The controlling section in the C++20 standard is in [class.this]:
In the body of a non-static member function, the keyword this is a prvalue whose value is the address of the object for which the function is called.
Nowhere in there (the entire section) does it mention that this is some sort of hidden parameter to the call.
And, regarding your question on why there is a distinction between non-static member functions and constructors, I don't believe this distinction involves the existence of this in either case, it instead has to do with the qualification of the type of this. It's existence in a constructor is undeniable as [class.ctor] states:
During the construction of an object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor’s this pointer, the value of the object or subobject thus obtained is unspecified.
In other words, I see your quote:
If the function is a non-static member function, the this parameter of the function is initialized with a pointer to the object of the call, converted as if by an explicit type conversion.
as specifying only the qualification of this, something that the constructor doesn't need.
There is no discussion of cv-qualified conversion for constructors as there is for other member functions because you can't actually create a cv-qualified constructor. It would be rather useless if your constructor were not allowed to set any member variables, for example :-)
While constructors can be used to create cv-qualified objects, the constructor itself is not cv-qualified. This is covered at the end of [class.this]:
Constructors and destructors shall not be declared const, volatile or const volatile. [Note: However, these functions can be invoked to create and destroy objects with cv-qualified types - end note]
And further in [class.ctor]:
A constructor can be invoked for a const, volatile or const volatile object. Const and volatile semantics are not applied on an object under construction. They come into effect when the
constructor for the most derived object ends.
To be honest, I think WG21 would be better off going through the next iteration and replacing things like "the this parameter of the function" with a phrase that does not mention parameters at all (such as "the this property".
Here's a quotation from this Draft C++17 Standard (bolding for emphasis, and to answer the question, is mine):
10.3.3 The using declaration      [namespace.udecl]
…
16    
For the purpose of forming a set of candidates during overload
resolution, the functions that are introduced by a using-declaration
into a derived class are treated as though they were members of the
derived class. In particular, the implicit this parameter shall
be treated as if it were a pointer to the derived class rather than to
the base class. This has no effect on the type of the function, and in
all other respects the function remains a member of the base class.
Likewise, constructors that are introduced by a using-declaration
are treated as though they were constructors of the derived class when
looking up the constructors of the derived class …
However I should add that the cited paragraph doesn't seem to be present in this later Draft Standard. In fact, that (later) Standard seems to use the phrase, "implicit object parameter," in similar clauses.
So, maybe you should add a specific version tag to your question: c++17 or c++20, as there appears to be a divergence in the use (or not) of the term.
Note that the above citation is the only occurrence of the phrase, "implicit this parameter" in that Draft Standard.
Also, note that both documents I have linked are only Draft versions of the respective Standards, and both come with this cautionary escape-clause:
Note: this is an early draft. It’s known to be incomplet and
incorrekt, and it has lots of bad formatting.

Are captureless lambda guaranteed to be empty by the standard?

I'm searching for a way to identify empty (captureless) lambdas from other lambdas in a template function. I'm currently using C++17 but I'm curious for C++20 answers too.
My code looks like this:
template<typename T>
auto func(T lambda) {
// The aguments of the lambdas are unknown
if constexpr (/* is captureless */) {
// do stuff
}
}
Is it guaranteed by the C++ standard (17 or 20) that a captureless lambda, which is convertible to a function pointer, will also make std::is_empty yield true?
Take this code as an example:
auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures
static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?
Live example
No, in fact, the standard explicitly grants permission for lambdas to have a size that doesn't line up with their declaration. [expr.prim.lambda.closure]/2 states
The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression. [ Note: This determines the set of namespaces and classes associated with the closure type ([basic.lookup.argdep]). The parameter types of a lambda-declarator do not affect these associated namespaces and classes. — end note ] The closure type is not an aggregate type. 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 ([class.prop]), or
(2.3)
whether the closure type is a standard-layout class ([class.prop]).
An implementation shall not add members of rvalue reference type to the closure type.
emphasis mine
So this allows the implementation to give the lambda a member even if it is capture-less. I don't think any implementation ever would, but they are legally allowed to do so.

Copying c++ lambda to Function Pointer reference

I am not sure if I have defined behaviour in the following situation:
My Function pointer type:
typedef void (*DoAfter_cb_type)(void);
The Function which should assign callbacks:
void DoSomething(DoAfter_cb_type & DoAfter_cb)
{
//...
DoAfter_cb = [](){
//...
};
}
Caller:
DoAfter_cb_type DoAfter_cb = nullptr;
DoSomething(DoAfter_cb);
// Here is something that has to be done after DoSomething but before DoAfter_cb.
if( DoAfter_cb != nullptr){
DoAfter_cb();
}
As I learned here lambdas can be implicitly converted to function pointers.
However thoose are still pointers and I fear that something important for calling the lambda is stored on stack and would be out of scope if I just return the function pointer
I have to use function pointers because i do not have access to std::function in my environment.
With std::function I would expect the lambda object to be stored in the reference variable and I would not have any problems.
Is the behaviour the same as If I would just define an ordinary function or do I have any side effects here?
Is the behaviour the same as If I would just define an ordinary function or do I have any side effects here?
Yes, it's the same. A captureless lambda is convertible to a regular function pointer because, to quote the C++ standard ([expr.prim.lambda.closure]/6, emphasis mine):
The closure type for a non-generic lambda-expression with no
lambda-capture 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 conversion is to “pointer
to noexcept function” if the function call operator has a non-throwing
exception specification. 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.
So while the lambda goes out of scope, that pointer is backed by a proper function, just as if you had written it yourself at file scope. Functions "live" throughout the entire execution of the program, so the pointer will be valid, always.

this in unevaluated context in static member functions

Why this is not allowed in unevaluated context in static member functions?
struct A
{
void f() {}
static void callback(void * self) // passed to C function
{
static_cast< decltype(this) >(self)->f();
}
};
This code gives an error:
error: 'this' is unavailable for static member functions
static_cast< decltype(this) >(self)->f();
^~~~
decltype(this) there is needed for brevity (sometimes it is much more shorter, then VeryVeryLongClassName *), another advantage is the fact that intention is more clear.
What Standard says about using this in unevaluated contexts in static member functions?
I don't see how it matters that this appears within an unevaluated context, you've referred to something that doesn't exist in a static member function, so how is the compiler supposed to deduce the type of this within this context?
As a corollary, the type of this in a non-static member function is dependent on the cv-qualifier of said member function, decltype(this) would yield T const* if the member function were const, and T * if it weren't. Thus, the type is dependent on the context of the expression. In your example, the context has no this pointer.
To alleviate the pain of having to name the class you could add an alias for it.
class VeryVeryLongClassName
{
using self = VeryVeryLongClassName;
};
The current draft of the Standard has some conflicting information.
If a declaration declares a member function or member function template of a class X, the expression this is a prvalue of type "pointer to cv-qualifier-seq X" wherever X is the current class between the optional cv-qualifier-seq and the end of the function-definition, member-declarator, or declarator.
It shall not appear within the declaration of either a static member function or an explicit object member function of the current class (although its type and value category are defined within such member functions as they are within an implicit object member function).
First it prohibits the expression this in a static member function, but then it says that the type and value category of this are defined within static member functions (and it does so in a way which resolves the question of cv qualification). Which seems useless if there is no way to refer to said this to obtain its type.
A potential resolution would be for the rule to change from "shall not appear" to "shall not be evaluated", which would immediately permit it to appear in unevaluated context such as decltype and sizeof.