Why are lambda expressions not allowed in an unevaluated operands but allowed in the unevaluated portions of constant expressions?

10,492

The core reason for the unevaluated operands exclusion is covered in C++ Standard Core Language Defect Reports and Accepted Issues #1607. Lambdas in template parameters which seeks to clarify this restriction and states the intention of the restriction in section 5.1.2 was to:

[...] avert the need to deal with them in function template signatures [...]

As the issue documents the current wording actually has a hole since constant expressions allows them in an unevaluated context. But it does not outright state the rationale for this restriction. The desire to avoid name mangling stands out and you can infer that avoiding extending SFINAE was also desired since the proposed resolution seeks to tighten the restriction even though several viable alternatives would have allowed SFINAE. The modified version of 5.1.2 paragraph 2 as follows:

A lambda-expression shall not appear in an unevaluated operand (Clause 5 [expr]), in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments [Note: The intention is to prevent lambdas from appearing in a signature —end note]. [Note: A closure object behaves like a function object (20.10 [function.objects]). —end note]

This proposal was accepted and is in N3936(see this answer for a link)

For a more explicit discussion of the rationale to avoid having lambdas as an unevaluated operand. The discussion titled Rationale for lambda-expressions not being allowed in unevaluated contexts on comp.lang.cpp.moderated Daniel Krügler lays out three reasons:

  1. The extreme extension of possible SFINAE cases :

[...]The reason why they became excluded was due to exactly this extreme extension of sfinae cases (you were opening a Pandora box for the compiler)[...]

  1. In many cases it is just useless since each lambda has a unique type, the hypothetical example given:

    template<typename T, typename U>
    void g(T, U, decltype([](T x, T y) { return x + y; }) func);
    
    g(1, 2, [](int x, int y) { return x + y; });
    

    The type of the lambda in the declaration and the call are different(by definition) and therefore this can not work.

  2. Name mangling also becomes a problem since once you allow a lambda in a function signature the bodies of the lambda will have to be mangled as well. This means coming up with rules to mangle every possible statement, which would burdensome for at least some implementations.

Share:
10,492

Related videos on Youtube

Shafik Yaghmour
Author by

Shafik Yaghmour

You can follow me on Twitter, @shafikyaghmour, if I recognize you from SO I will probably follow back. If you enjoy my answers and questions here then you will probably enjoy my weekly Twitter quizzes.

Updated on June 06, 2022

Comments

  • Shafik Yaghmour
    Shafik Yaghmour almost 2 years

    If we look at the draft C++ standard section 5.1.2 Lambda expressions paragraph 2 says (emphasis mine going forward):

    The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A closure object behaves like a function object (20.8).—end note ]

    and section 5.19 Constant expressions paragraph 2 says:

    A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [...]

    and has the following bullet:

    — a lambda-expression (5.1.2);

    So why are lambdas expressions not allowed in an unevaluated operand but are allowed in the unevaluated portions of constant expressions?

    I can see how for unevaluated operands the type information in several cases(decltype or typeid) is not very useful since each lambda has a unique type. Although why we would want to allow them in the unevaluated context of a constant expression is not clear, perhaps to allow for SFINAE?

  • dyp
    dyp about 10 years
    n3936 seems to have been password-protected (fdis?), links is also broken.
  • Shafik Yaghmour
    Shafik Yaghmour about 10 years
    @dyp hmmm, it worked this morning, oh well. Loki also linked to that one here so it may be temporary let me see if I can figure out what is going on.
  • dyp
    dyp about 10 years
  • Shafik Yaghmour
    Shafik Yaghmour about 7 years
    Also see D0315R2
  • Shafik Yaghmour
    Shafik Yaghmour over 6 years
    It looks like p0315r3 was accepted for C++20