When is a lambda a guaranteed to be trivial, if ever?
I assumed that if it captures only trivial types or nothing, it would be trivial. I don't have any standard-ese to back that up though.
My motivation was in moving some code from Visual C++ 12 to 14 and discovered some static asserts failed when dealing with lambdas I assumed to be trivial.
Example:
#include <type_traits>
#include <iostream>
using namespace std;
int main()
{
auto lambda = [](){};
cout << boolalpha << is_trivially_copyable<decltype(lambda)>{} << endl;
}
This produces false on vs140 but true in vs120 and clang. I could not test gcc due to not having gcc >= 5 around. I expect this is a regression in vs140, but I'm not certain of the correct behavior here.
The standard does not specify whether a closure type (the type of a lambda expression) is trivial or not. It explicitly leaves this up to implementation, which makes it non-portable. I am afraid you cannot rely on your static_assert producing anything consistent.
Quoting C++14 (N4140) 5.1.2/3:
... 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).
...
(Emphasis mine)
After parsing the double negation in that sentence, we can see that the implementation is allowed to decide whether the closure type is trivially copyable, standard layout, or POD.
Note that the same wording is also present in C++17 (N4659), [expr.prim.lambda.closure] 8.1.5.1/2.
According to the draft standard N4527 5.1.2/3 Lambda expressions [expr.prim.lambda] (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. This class type
is neither an aggregate (8.5.1) nor a literal type (3.9). 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 (3.4.2). The parameter types of a
lambdadeclarator do not affect these associated namespaces and
classes. — end note ] 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:
(3.1) — the size and/or alignment of the closure type,
(3.2) — whether the closure type is trivially copyable (Clause 9),
(3.3) — whether the
closure type is a standard-layout class (Clause 9), or
(3.4) — whether
the closure type is a POD class (Clause 9).
An implementation shall
not add members of rvalue reference type to the closure type
Thus, it's implementation dependent.
Related
Both gcc and clang accept the following code, and I'm trying to figure out why.
// c++ -std=c++20 -Wall -c test.cc
#include <concepts>
struct X {
int i;
};
// This is clearly required by the language spec:
static_assert(std::same_as<decltype(X::i), int>);
// This seems more arbitrary:
static_assert(std::same_as<decltype((X::i)), int&>);
The first static_assert line makes sense according to [dcl.type.decltype]:
otherwise, if E is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]), decltype(E) is the type of the entity named by E. If there is no such entity, or if E names a set of overloaded functions, the program is ill-formed;
- https://timsong-cpp.github.io/cppwp/n4861/dcl.type.decltype#1.3
X::i is a valid id-expression in an unevaluated context, so its decltype should be the declared type of i in X.
The second static_assert has me stumped. There's only one clause in [dcl.type.decltype] in which a parenthesized expression yields an lvalue reference: it must be that both compilers consider X::i to be an expression of lvalue category. But I can't find any support for this in the language spec.
Obviously if i were a static data member then X::i would be an lvalue. But for a non-static member, the only hint I can find is some non-normative language in [expr.context]:
In some contexts, unevaluated operands appear ([expr.prim.req], [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple], [temp.pre], [temp.concept]).
An unevaluated operand is not evaluated.
[ Note: In an unevaluated operand, a non-static class member may be named ([expr.prim.id]) and naming of objects or functions does not, by itself, require that a definition be provided ([basic.def.odr]).
An unevaluated operand is considered a full-expression.
— end note
]
- https://timsong-cpp.github.io/cppwp/n4861/expr.prop#expr.context-1
This suggests decltype((X::i)) is a valid type, but the definition of full-expression doesn't say anything about value categories. I don't see what justifies int& any more than int (or int&&). I mean an lvalue is a glvalue, and a glvalue is "an expression whose evaluation determines the identity of an object." How can an expression like X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
Are gcc and clang right to accept this code, and if so what part of the language specification supports it?
remark:
StoryTeller - Unslander Monica's answer makes even more sense in light of the fact that sizeof(X::i) is allowed and that decltype((X::i + 42)) is a prvalue.
How can an expression like X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
That's not entirely true. That expression can be evaluated (following a suitable transformation in the right context).
[class.mfct.non-static]
3 When an id-expression that is not part of a class member access syntax and not used to form a pointer to member ([expr.unary.op]) is used in a member of class X in a context where this can be used, if name lookup resolves the name in the id-expression to a non-static non-type member of some class C, and if either the id-expression is potentially evaluated or C is X or a base class of X, the id-expression is transformed into a class member access expression using (*this) as the postfix-expression to the left of the . operator.
So we can have something like
struct X {
int i;
auto foo() const { return X::i; }
};
Where X::i is transformed into (*this).X::i. Which is explicitly allowed on account of
[expr.prim.id]
2 An id-expression that denotes a non-static data member or non-static member function of a class can only be used:
as part of a class member access in which the object expression refers to the member's class or a class derived from that class, or ...
And that meaning of (*this).X::i is always an lvalue denoting the class member i.
So you see, in the contexts where X::i can be evaluated, it always produces an lvalue. So decltype((X::i)) in those context would need to be an lvalue reference type. And while outside of class scope X::i cannot generally be used in an expression, it's still not entirely arbitrary to say its value category is an lvalue. We are just expanding the region of definition a bit (which doesn't contradict the standard, since the standard doesn't define it).
How can an expression like `X::i--which can't be evaluated at all, let alone determine the identity an object--be considered a glvalue?
Ignoring the misuse of «result», it is [expr.prim.id.qual]/2:
A nested-name-specifier that denotes a class, optionally followed by the keyword template ([temp.names]), and then followed by the name of a member of either that class ([class.mem]) or one of its base classes, is a qualified-id; ... The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise.
The value category of an expression is not determined by the «definition» in [basic.lval], like «an expression whose evaluation determines the identity of an object», but specified for each kind of expression explicitly.
The root of your question seems to be the difference between decltype(X::i) and decltype((X::i)). Why does (X::i) yield a int&? See:
https://timsong-cpp.github.io/cppwp/n4861/dcl.type.decltype#1.5
otherwise, if E is an lvalue, decltype(E) is T&, where T is the type of E;
However, the key point here is that the fact that it yields a T& doesn't really matter:
https://timsong-cpp.github.io/cppwp/n4861/expr.type#1
If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression. [ Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see [basic.life]). — end note ]
What "justifies" it then? Well, when doing decltype(X::i) we're concerned primarily with what the type of X::i is and not its value category or its properties when treated as an expression. However, (X::i) is there if we do care.
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 have been looking for this in N4713 for more than two hours to no avail.
I have a C++14 draft which says, at 7.1.6.1 [dcl.type.cv]:
As described in 8.5, the definition of an object or subobject of
const-qualified type must specify an initializer or be subject to
default-initialization
8.5 [dcl.init] says (clause 7):
To default-initialize an object of type T means:
[for a non class, non array type]:
... no initialization is performed
And immediately follows with:
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type
with a user-provided default constructor.
Therefore a const built-in must have an initializer, since otherwise it would be default-initialized and that is not allowed.
It's worded in quite an indirect way in N4713 [dcl.init] 11.6/7:
If a program calls for the default-initialization of an object of a const-qualified type T, T shall be a const-default-
constructible class type or array thereof.
Not providing an initialiser causes an object to be default-initialised. As such, if it's const qualified and of a built-in type, it violates the "shall" rule above.
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)