Is a lambda expression a legal default (non-type template) argument?

Viewed 1079

All standard references below refers to N4861 (March 2020 post-Prague working draft/C++20 DIS).


Background

In the Q&A Are captureless lambdas structural types? it was made clear that certain lambda-expressions have associated closure types that are (literal and) structural types, such that a particular such closure type may be used as non-type template parameter; essentially passing structural type lambdas as non-type template parameters.

template<auto v>
constexpr auto identity_v = v;

constexpr auto l1 = [](){};
constexpr auto l2 = identity_v<l1>;

Now, according to [expr.prim.lambda.closure]/1 the type of each lambda-expression is unique

[...] a unique, unnamed non-union class type, called the closure type [...]

On the other hand, [basic.def.odr]/1 [extract, emphasis mine] states

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, template, default argument for a parameter (for a function in a given scope), or default template argument.

arguably meaning that default template arguments are considered definitions that need to respect the ODR.

Question

... which leads to my question:

  • Is a lambda expression a legal default (non-type template) argument and, if so, wouldn't this imply that each instantiation using such a default argument instantiates a unique specialization?

(Please highlight also if near-illegal: e.g. if anything beyond a single instantiation would lead to an ODR-violation).


Why?

If this is in fact legal, each invocation of say a variable template with a lambda as default argument would result in an instantiation of a unique specialization:

template<auto l = [](){}>
               // ^^^^^^ - lambda-expression as default argument
constexpr auto default_lambda = l;

static_assert(!std::is_same_v<
    decltype(default_lambda<>),
    decltype(default_lambda<>)>);

Both GCC (DEMO) and Clang (DEMO) accepts the program above

If the compilers are correct to accept this example, this means allowing another mechanism to capture and retrieve a meta-programming state, a technique that has since long been deemed, as per CWG open issue 2118, as

... arcane and should be made ill-formed.

1 Answers

Is a lambda expression a legal default (non-type template) argument and, if so, wouldn't this imply that each instantiation using such a default argument instantiates a unique specialization?

Yes, it means that given your templated variable:

template<auto l = [](){}>
constexpr auto default_lambda = l;

every call to default_lambda will produce a new template instantiation by generating a new unique closure type and thus provides a new way to capture a metaprogramming state.

Thus, every expressions that use default_lambda have to be reevaluated. If the state of the compiler changed since the last instantiation and if we used it in a dependent expression (ex: check that a type is defined) then the result of the expression might change. For example:

struct X; // not defined

template <typename T, auto = default_lambda<>>
consteval bool is_defined() {
    if constexpr (requires { T{}; }) {
        return true;
    } else {
        return false;
    }
}

static_assert(is_defined<X>() == false);

struct X {};
static_assert(is_defined<X>() == true);
Related