Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the requirements to make a lambda constexpr?

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.

like image 731
Enlico Avatar asked Oct 30 '25 15:10

Enlico


1 Answers

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.

like image 50
Jan Schultke Avatar answered Nov 01 '25 06:11

Jan Schultke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!