Every lambda should have a unique unknown type.. is it guaranteed that two lambdas into the same scope have different types?
int main() {
auto l1 = [](){};
auto l2 = [](){};
static_assert(std::is_same<decltype(l1), decltype(l2)>::value == false, "Types cannot be equal!");
}
This works but I'd like to know if it's guaranteed that the assert will never fire.
Yes, each lambda expression produces a unique type ([expr.prim.lambda]/3):
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.
[emphasis added]
Related
I am following this tutorial. The following declaration found in main.cc has me intrigued:
auto say_hello = [](const HttpRequest& request) -> HttpResponse {
HttpResponse response(HttpStatusCode::Ok);
response.SetHeader("Content-Type", "text/plain");
response.SetContent("Hello, world\n");
return response;
}
This is shown in the debugging window.
I wish to replace the auto keyword with the original data type. I have tried the following but it fails:
HttpResponse say_hello = [](const HttpRequest& request) -> HttpResponse {...}
Could someone tell me why it is wrong? What is the correct solution? Thank you so much!!!
I think it's important to know the 3 following methods when working with lambdas.
The three firsts do essentially the same thing: storing the lambda for future use.
The other seems to be what you expect from your code: you directly call the lambda and get the result, so the return type is the type returned by the lambda.
#include <iostream>
#include <functional>
int main()
{
// Auto store the lambda
auto auto_keyword = [](int a) -> int
{ return a; };
std::cout << auto_keyword(42) << std::endl;
// std::function store the lambda
std::function<int(int)> std_function = [](int a) -> int
{ return a; };
std::cout << std_function(42) << std::endl;
// raw function pointer
int (*raw_fptr)(int) = [](int a) -> int
{ return a; };
std::cout << raw_fptr(42) << std::endl;
// direct call to the lambda. (what you expect from your question)
std::cout << ([](int a) -> int{ return a; })(42) << std::endl;
return 0;
}
A lot of things happen here, and I suggest you to read at least this page from the reference for a better understanding.
According to cppreference, (emphasize mine)
The lambda expression is a prvalue expression of unique unnamed non-union non-aggregate class type, known as closure type
As the type of a lambda expression is an unnamed type you cannot use it in a declaration. That is the reason why auto is the common usage for a lambda.
If you want to be more explicit for the arguments and return type, you can store the lambda in a std::function variable:
std::function<HttpResponse(const HttpRequest&)> say_hello = ...
But std::function is still not really the type of the lambda (which is unnamed...), it is just the type of a variable which is able to hold that lambda, as it could old the address of a plain function.
For further reference, draft n4860 for C++20 says at 7.5.5.1 Closure types [expr.prim.lambda.closure] §1
The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union
class type, called the closure type ...
From C++ standard
[C++11: 5.1.2/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. This
class type is not an aggregate (8.5.1). The closure type is declared
in the smallest block scope, class scope, or namespace scope that
contains the corresponding lambda-expression.
In short each instance of a lambda is its own unique unnamed type.
[C++11: 5.1.2/5]: The closure type for a lambda-expression has a
public inline function call operator (13.5.4) whose parameters and
return type are described by the lambda-expression’s
parameter-declaration-clause and trailing-return-type respectively.
Since lambda has function call operator defined you can assign it to std::function
[C++11: 5.1.2/6]: The closure type for a lambda-expression with no
lambda-capture has a public non-virtual non-explicit const conversion
function to pointer to function having the same parameter and return
types as the closure type’s function call operator. The value returned
by this conversion function shall be the address of a function that,
when invoked, has the same effect as invoking the closure type’s
function call operator.
If the lambda does not capture anything it can be converted to a function pointer.
Normally, a lambda expression is an anonymous function object and it doesn't have a named type. However, sometimes we can use type erasure. If a lambda expression doesn't capture any arguments like your example, it can convert to function pointer implicitly. If a lambda expression captures some arguments, it can convert to std::function.
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?
But is the program well-formed according to the standard ...
The program has undefined behaviour conditional on leeway given to implementors. Particularly conditional on whether the closure type of the lambda
[this](){ return v; };
is trivially copyable from; as per [expr.prim.lambda.closure]/2:
The closure type is declared in the smallest block scope, class scope,
or namespace scope that contains the corresponding lambda-expression. [...]
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:
(2.1) the size and/or alignment of the closure type,
(2.2) whether the closure type is trivially copyable ([class.prop]), or
(2.3) whether the closure type is a standard-layout class ([class.prop]). [...]
This means that whether the constraints of [bit.cast]/1 are fulfilled or not:
template<class To, class From>
constexpr To bit_cast(const From& from) noexcept;
Constraints:
(1.1) sizeof(To) == sizeof(From) is true;
(1.2) is_trivially_copyable_v<To> is true; and
(1.3) is_trivially_copyable_v<From> is true.
is implementation-defined.
... and is such 'construction' of lambda objects valid?
As [expr.prim.lambda.closure]/2.1 also states that the size and alignment of the closure type is implementation-defined, using std::bit_cast to create an instance of the closure type may result in a program with undefined behavior, as per [bit.cast]/2:
Returns: An object of type To. Implicitly creates objects nested within the result ([intro.object]). Each bit of the value representation of the result is equal to the corresponding bit in the object representation of from. Padding bits of the result are unspecified. For the result and each object created within it, if there is no value of the object's type corresponding to the value representation produced, the behavior is undefined. If there are multiple such values, which value is produced is unspecified.
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.
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.
I can't find anything about throwing exceptions during constructing closure object.
It's oblivious that this expression can throw during copy construction of the vector:
auto v = std::vector<int>(1000000);
[v]{};
But what about empty or "by reference" capture lists like this:
[&]{};
I'm speaking only about construction of closure object now. Calling isn't interesting.
I read 5.1.2 Lambda expressions [expr.prim.lambda], but found nothing special about nothrow guarantee.
According to the standard (draft n3337), §5.1.2/3:
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. This class type is not
an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace
scope that contains the corresponding lambda-expression.
In a nutshell, a lambda expression ultimately instantiates an anonymous type (known only to the compiler), containing member variables that store the values indicated in the capture list. So imagine you saw a class that looked like this:
class __IgnoreMe__
{
std::vector<int>& _v;
public:
__IgnoreMe__(std::vector<int>& v)
: _v(v)
{
}
void operator()()
{
}
};
(Note: this isn't exactly how the class created by the compiler looks. The standard lays out specific requirements for generated classes, which I've left out for brevity's sake.)
Now imagine you instantiate that class like this:
auto v = std::vector<int>(1000000);
auto my_lambda = __IgnoreMe__(v);
Can the instantiation of my_lambda throw an exception? If so, then the construction of the closure object can throw an exception. (It can't, in this case.)
As for providing nothrow guarantees, the standard doesn't require compilers to account for this, but it doesn't prevent them from doing it either. The end of §5.1.2/3 states:
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).
This question already has answers here:
What is the type of lambda when deduced with "auto" in C++11?
(8 answers)
Closed 6 years ago.
There is this code:
auto fun = [](int x)->int {return x + 1; };
std::cout << typeid(fun).name() << std::endl;
The result is: Z4mainEUliE_ but c++filt doesn't seem to explain what is it. What is type of lambda expression?
§5.1.2/3 states:
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type
It goes on to say more, but that's the most important bit. A lambda is basically an instance of an anonymous class.
Incidentally, the demangled form of your lambda is main::$_0.
The type of a lambda function is unspecified by the standard (§5.1.2):
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union classtype — called the closure type — whose properties are described below. This class type is not an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace scope that contains the corresponding lambda-expression.
It then goes on listing the exact properties a closure type should have.
Therefore there is no general type for a lambda function to have. The compiler will generate a new functor type with unspecified name for each lambda function
What is type of lambda expression?
The type of a lambda expression (the so-called closure) is an unnamed class type with a function call operator automatically generated by the compiler. The internal name the compiler will give it is unspecified.
According to Paragraph 5.1.2/3 of the C++11 Standard:
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. This class type is not
an aggregate (8.5.1). The closure type is declared in the smallest block scope, class scope, or namespace
scope that contains the corresponding lambda-expression. [...]
Also notice, that the name() member function of the type_info class (the type returned by typeid()) is also implementation-dependent, and the Standard does not require it to be meaningful for a human.
Per Paragraph 18.7.1:
const char* name() const noexcept;
9 Returns: An implementation-defined NTBS.
10 Remarks: The message may be a null-terminated multibyte string (17.5.2.1.4.2), suitable for conversion and display as a wstring (21.3, 22.4.1.4)