A colleague showed me a C++20 program where a closure object is virtually created using std::bit_cast from the value that it captures:
#include <bit>
#include <iostream>
class A {
int v;
public:
A(int u) : v(u) {}
auto getter() const {
if ( v > 0 ) throw 0;
return [this](){ return v; };
}
};
int main() {
A x(42);
auto xgetter = std::bit_cast<decltype(x.getter())>(&x);
std::cout << xgetter();
}
Here main function cannot call x.getter() due to exception. Instead it calls std::bit_cast taking as template argument the closure type decltype(x.getter()) and as ordinary argument the pointer &x being captured for new closure object xgetter. Then xgetter is called to obtain the value of object x, which is otherwise not accessible in main.
The program is accepted by all compilers without any warnings and prints 42, demo: https://gcc.godbolt.org/z/a479689Wa
But is the program well-formed according to the standard and is such 'construction' of lambda objects valid?
The program has undefined behaviour conditional on leeway given to implementors. Particularly conditional on whether the closure type of the lambda
is trivially copyable from; as per [expr.prim.lambda.closure]/2:
This means that whether the constraints of [bit.cast]/1 are fulfilled or not:
is implementation-defined.
As [expr.prim.lambda.closure]/2.1 also states that the size and alignment of the closure type is implementation-defined, using
std::bit_castto create an instance of the closure type may result in a program with undefined behavior, as per [bit.cast]/2:For any kind of practical use, however, I'd argue that if a construct has undefined behavior conditional on implementation leeway details (unless these can be queried with say traits), then the construct should reasonably be considered to have undefined behavior, except possibly for a compiler's internal C++ (e.g. Clang frontend) implementation, where these implementation details are known.