All standard references below refers to N4861 (March 2020 post-Prague working draft/C++20 DIS).
The Q&A Are captureless lambdas structural types? highlighted the potential underspecification of whether certain lambda-expressions have associated closure types that are (literal and) structural types or not. The impact of this classification could allow or reject using a closure type 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.
... which leads to my question:
(Please highlight also if near-illegal: e.g. if anything beyond a single instantiation would lead to an ODR-violation).
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.
As per of the resolution to CWG 2845, captureless a lambdas are structural types again, meaning there seems to be nothing prohibiting lambda expressions as a legal default (non-type template) argument.
It may still be an open questions about how this opens up the non-intended abuse potential of stateful meta-programming becoming more wide-spread due to this new "tool".
2845. Make the closure type of a captureless lambda a structural type
[...]
Proposed resolution (approved by CWG 2024-02-02):
Change in 7.5.5.2 [expr.prim.lambda.closure] paragraph 3 as follows:
The closure type is not an aggregate type (9.4.2 [dcl.init.aggr]) and not ; it is a structural type (13.2 [temp.param]) if and only if the lambda has no lambda-capture. An implementation may define the closure type differently from ...
As per the resolution of CWG 2542 it was made clear that lambdas are not structural types, no matter their capture. So for some time, there was a clear answer to this question:
2542. Is a closure type a structural type?
Consider:
template <auto V> void foo() {} void bar() { foo<[i = 3] { return i; }>(); }
It is unclear whether the data members of a closure type are public or private. This makes a difference, since it affects whether a closure type is a structural type or not [...]
[...]
Proposed resolution (approved by CWG 2023-03-30):
Change in 7.5.5.2 [expr.prim.lambda.closure] paragraph 2 as follows:
... The closure type is not an aggregate type (9.4.2 [dcl.init.aggr]) and not a structural type (13.2 [temp.param]). ...