Several times, in code review, I'm told to add contexpr to some named lambda closure declaration, i.e. I'm told to change this
auto lam = [capture list](args){body}
to this:
constexpr auto lam = [capture list](args){body}
Therefore, I'd like to understand what the conditions are that allow a lambda closure to be declared as constexpr, so I can make this change autonomously.
Here I read that
A constexpr variable must satisfy the following requirements:
- its type must be a LiteralType.
- it must be immediately initialized
- the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
- [a C++20-specific condtion]
Where I see that the second condition is verified, but I'd like some help understanding the first and especially the third condition.
Concerning the first condition, at the LiteralType page I understand that it is enough, for a variable to be a LiteralType, to be a possibly cv-qualified class type that has a destructor (how could it not have one?) and is a closure type (which is the case for the lam above), and all non-static data members and base classes are of non-volatile literal types (I'm not sure about this last part in relation to lambdas).
The bottom line is that I'd like to understand how I can understand, by inspection, if I can make a lambda constexpr.
The only thing that decides whether a lambda can be constexpr is the capture list. The body can be non-constexpr and a constexpr lambda can still have parameters like std::string, which are not literal types either.
its type must be a LiteralType.
The LiteralType requirement for lambdas effectively means that all captured members have to be non-volatile literal types too. Defining a lambda is equivalent to defining an anonymous struct where the captured variables are members.
constexpr int x = 0;
constexpr auto lam = [x](){};
// equivalent to:
struct Lam {
int x;
void operator()() {}
};
constexpr Lam lam2{x};
When we think of lambdas as such structs with call operators, it also becomes obvious what all these requirements mean. We can't capture a non-literal type, because that would require storing a non-literal type in this struct. And that can't happen in a constexpr context.
the full-expression of its initialization, including all implicit conversions, constructors calls, etc, must be a constant expression
Once again, this basically states that capturing variables has to happen in a constexpr way. All captured variables must be constexpr, otherwise the initialization won't be. In fact, constexpr variables are all implicitly captured. So what this really means is that a lambda can be made constexpr exactly when it doesn't have a capture list, or that capture list could be omitted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With