lambda 表达式是合法的默认(非类型模板)参数吗?

dfr*_*fri 33 c++ lambda templates language-lawyer c++20

以下所有标准参考均指N4861(2020 年 3 月布拉格后工作草案/C++20 DIS)


背景

在问答中 无捕获的 lambda 是结构类型吗?很明显,某些 lambda 表达式具有关联的闭包类型,这些闭包类型是(文字和)结构类型,因此特定的此类闭包类型可以用作非类型模板参数;本质上将结构类型 lambda 作为非类型模板参数传递。

template<auto v>
constexpr auto identity_v = v;

constexpr auto l1 = [](){};
constexpr auto l2 = identity_v<l1>;
Run Code Online (Sandbox Code Playgroud)

现在,根据[expr.prim.lambda.closure]/1 ,每个 lambda 表达式的类型都是唯一的

[...] 一个独特的、未命名的非联合类类型,称为闭包类型[...]

另一方面,[basic.def.odr]/1 [extract,强调我的]指出

任何翻译单元不得包含任何变量、函数、类类型、枚举类型、模板、参数的默认参数(对于给定范围内的函数)或默认模板参数的多个定义

可以说,默认模板参数被认为是需要尊重 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<>)>);
Run Code Online (Sandbox Code Playgroud)

GCC(DEMO)和Clang(DEMO)都接受上面的程序

如果编译器接受这个例子是正确的,这意味着允许另一种机制来捕获和检索元编程状态,根据CWG 公开问题 2118长期以来一直被认为是一种技术,如

... 神秘的,应该是畸形的。

dfr*_*fri 2

,它不是合法的默认非类型模板参数,因为根据CWG 2542的决议,lambda 的闭包类型不是结构类型。

2542. 闭包类型是结构类型吗?

考虑:

template <auto V>
void foo() {}

void bar() {
  foo<[i = 3] { return i; }>();
}
Run Code Online (Sandbox Code Playgroud)

目前尚不清楚闭包类型的数据成员是公共的还是私有的。这很重要,因为它会影响闭包类型是否是结构类型 [...]

[...]

拟议决议(经 CWG 2023-03-30 批准):

7.5.5.2 [expr.prim.lambda.closure] 第 2 段的更改如下:

...闭包类型不是聚合类型(9.4.2 [dcl.init.aggr]),也不是结构类型(13.2 [temp.param])。...