背景
Q&A 捕获无效的lambda表达式是否是结构类型? 强调了某些lambda表达式是否具有关联的闭包类型(文字和)结构类型的潜在规范不足。这种分类的影响可以允许或拒绝将闭包类型用作非类型模板参数;从本质上讲,将结构类型的lambda表达式作为非类型模板参数传递。
template<auto v>
constexpr auto identity_v = v;
constexpr auto l1 = [](){};
constexpr auto l2 = identity_v<l1>;
现在,根据[expr.prim.lambda.closure]/1,每个lambda表达式的类型是独一无二的。
另一方面,[basic.def.odr]/1(摘录,重点是我的)指出:[...] 一个独一无二的、无名的非联合类类型,称为闭包类型 [...]
可以说默认模板参数被认为是需要遵守ODR的定义。任何翻译单元都不得包含超过一个定义的变量、函数、类类型、枚举类型、模板、参数的默认参数(对于给定作用域中的函数),或默认模板参数。
问题
这就引出了我的问题:一个lambda表达式是否是一个合法的默认(非类型模板)参数?如果是的话,这是否意味着每个使用这样一个默认参数的实例化都会实例化一个独特的特化版本?
(如果超出单个实例化会导致ODR违规,请也进行标注)。
为什么?
如果这确实是合法的,每次调用带有lambda作为默认参数的变量模板都会导致实例化一个独特的特化版本。
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<>)>);
GCC(DEMO)和Clang(DEMO)都接受上述程序。
如果编译器正确地接受了这个例子,这意味着允许另一种机制来捕获和检索元编程状态,这种技术在CWG开放问题2118中长期被认为是
... 难以理解并且应该被视为无效。