【发布时间】:2021-02-04 09:39:18
【问题描述】:
以下所有标准参考均指N4861 (March 2020 post-Prague working draft/C++20 DIS)。
背景
在 Q&A Are captureless lambdas structural types? 中明确指出,某些 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 表达式的类型是唯一的
[...] 一个unique,未命名的非联合类类型,称为闭包类型 [...]
另一方面,[basic.def.odr]/1 [extract, emphasis mine] 声明
任何翻译单元不得包含超过任何变量、函数、类类型、枚举类型、模板、参数的默认参数(对于给定范围内的函数)的定义,或 默认模板参数。
可能意味着默认模板参数被认为是需要尊重 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 open issue 2118,这项技术长期以来被认为是
... 晦涩难懂,应该是格式错误的。
【问题讨论】:
-
[temp.decls]/2 是关于默认的 function 参数(例如
void foo(int = 0);)。它不适用于默认模板参数。确切地说,该标准在适当的时候使用“默认模板参数”,它有意避免草率使用该术语。 -
@StoryTeller-UnslanderMonica 谢谢,我更新为引用[basic.def.odr]/1,它描述了(但是在较弱的意义上/间接地)默认模板参数是定义。
-
我相信this motion 解决了这个问题,但我几乎不理解那个标准措辞。
-
我认为因为没有捕获的 lambda 现在是像没有成员的结构这样的微不足道的类型,所以 lambda 可以像模板参数一样传递。我不知道可能违反 ODR。这些可能会导致模板的多个实例化,而预期只有一个。
-
hm,模板只能在 lambda 表达式只能无捕获的范围内声明,因此不会发生任何“神秘”事件?
标签: c++ templates lambda language-lawyer c++20