All standard references below, unless explicitly noted otherwise, refers to N4861 (March 2020 post-Prague working draft/C++20 DIS) https://timsong-cpp.github.io/cppwp/n4861/.
无捕获 lambda 的类型(其闭合类型) is a 结构类型
从今以后,我们将 lambda 的类型单独称为闭合类型.
正如下面的标准段落所示,无捕获 lambda 的闭包类型:
- 满足其成为一个的要求文字(类)类型,而且此外
- 满足文字类型的要求,使其成为结构类型,
因此可以用作非类型模板参数的类型,使得示例片段
template<auto v>
constexpr auto identity_v = v;
constexpr auto l1 = [](){};
constexpr auto l2 = identity_v<l1>;
确实是格式良好的。
lambda 的闭包类型是非联合类类型
受以下管辖[expr.prim.lambda.closure]/1 https://timsong-cpp.github.io/cppwp/n4861/expr.prim.lambda.closure#1 [emphasis mine]
的类型lambda 表达式(这也是闭包对象的类型)是唯一的、未命名的非联合类类型,称为闭包类型,其属性如下所述。
闭合类型是非联合类类型.
无捕获 lambda 的闭包类型是文字(类)类型
受以下管辖[基本类型]/10 https://timsong-cpp.github.io/cppwp/n4861/basic.types#10[提炼,emphasis mine]
类型是一个文字类型如果是:
- [...]
- a possibly cv-qualified class type that has all of the following properties:
-
它有一个 constexpr 析构函数([dcl.constexpr]),
-
it is either 闭合类型([expr.prim.lambda.closure])、聚合类型 ([dcl.init.aggr]),或至少有一个 constexpr
构造函数或构造函数模板(可能从基类继承
类)不是复制或移动构造函数,
- 如果它是联合体,则其至少一个非静态数据成员是非易失性文字类型,并且
- 如果它不是联合,则其所有非静态数据成员和基类都是非易失性文字类型。
闭包类型是文字类型如果
- 有一个 constexpr 析构函数,并且如果
- 它的所有非静态数据成员都是非易失性文字类型。
无捕获 lambda 的闭包类型没有非静态数据成员,因此满足后一个要求。前者怎么样,constexpr 析构函数?
隐式生成的 constexpr 析构函数
受以下管辖[expr.prim.lambda.closure]/14 https://timsong-cpp.github.io/cppwp/n4861/expr.prim.lambda.closure#14
与 a 关联的闭包类型lambda 表达式有一个隐式声明的析构函数([class.dtor])。
闭包类型的析构函数是隐式声明的。此外,[/dcl.fct.def.default]/5 https://timsong-cpp.github.io/cppwp/n4861/dcl.fct.def.default#5描述[摘录,emphasis mine]
显式默认函数和隐式声明函数统称为违约功能,以及实施应为它们提供隐式定义([class.ctor],[类.dtor], [class.copy.ctor], [class.copy.assign]), [...]
即集体术语违约函数还包括隐式声明的析构函数。
最后,[class.dtor]/9 https://timsong-cpp.github.io/cppwp/n4861/class.dtor#9
如果默认析构函数满足 constexpr 析构函数 ([dcl.constexpr]) 的要求,则它是 constexpr 析构函数。
描述默认析构函数是 constexpr 析构函数,如果它们满足以下要求[dcl.constexpr] https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr, 特别[dcl.constexpr]/3 https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr#3 and [dcl.constexpr]/5 https://timsong-cpp.github.io/cppwp/n4861/dcl.constexpr#5[摘录,emphasis mine]
[dcl.constexpr]/3constexpr 函数的定义应满足以下要求:
- [...]
-
如果函数是构造函数或析构函数,其类不得有任何虚拟基类;
- [...]
[dcl.constexpr]/5constexpr 析构函数的定义,其函数体 is not = delete
还应满足以下要求:
- 对于类类型或其(可能是多维)数组的每个子对象,该类类型应具有 constexpr 析构函数。
所有这些都满足无捕获 lambda 的闭包类型(没有基类,也没有子对象;请参阅[介绍对象]/2 https://timsong-cpp.github.io/cppwp/n4861/intro.object#2对于后者)。
因此,无捕获 lambda 的闭包类型是文字类型。
无捕获 lambda 的闭包类型是结构类型
As per [温度参数]/6 https://timsong-cpp.github.io/cppwp/n4861/temp.param#6 and [温度参数]/7 https://timsong-cpp.github.io/cppwp/n4861/temp.param#7[提炼,emphasis mine]
[温度参数]/6非类型模板参数应具有以下类型之一(可能是 cv 限定的):
[温度参数]/7
A 结构类型是以下之一:
- 标量类型,或
- 左值引用类型,或者
- a literal class type with the following properties:
- 所有基类和非静态数据成员都是公共且不可变的
- 所有基类和非静态数据成员的类型都是结构类型或其(可能是多维)数组。
如果文字类类型没有基类和非静态数据成员,那么它就是一种结构类型。这两者都适用于无捕获 lambda,因此,无捕获 lambda 的闭包类型是结构类型。
关于原文的一些注释intent允许 lambda 的闭包类型为文字类型
N4487 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4487.pdf提议允许某些 lambda 表达式和某些闭包对象上的操作出现在常量表达式中,并包含一个专门的部分来讨论闭包类型是文字类型的主题:
如果闭包对象的每个数据成员的类型都是文字类型,则该闭包对象应该是文字类型。
C++14 中的闭包类型永远不可能是文字类型 – 即使它的所有
数据成员是文字类型——因为它缺少 constexpr
不是复制或移动构造函数的构造函数。如果这样的关闭
类型被允许有一个隐式定义的默认构造函数
将是 constexpr,使其成为文字类型。但是,因为关闭
根据定义,类型必须删除其默认构造函数,
禁止实现隐式定义一个。 [...]
P0170R1 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0170r1.pdf包含 N4487 的核心措辞,已被 C++17 接受并实现。
然而,此时(C++14 和 C++17)析构函数不能是 constexpr,因此自然不要求文字类型具有 constexpr 析构函数;[基本类型]/10.5.1 https://timsong-cpp.github.io/cppwp/n4140/basic.types#10.5.1在 N4140 (C++14) 以及[基本类型]/10.5.1 https://timsong-cpp.github.io/cppwp/n4659/basic.types#10.5.1在 N4659 (C++17) 中,要求析构函数很简单:
如果某个类型满足以下条件,则该类型是文字类型:
- [...]
- a class type (Clause [class]) that has all of the following properties:
P1907R1 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1907r1.html,为 C++20 所接受,扩展了以下要求模板参数对象不断的破坏;[温度参数]/8 https://timsong-cpp.github.io/cppwp/n4861/temp.param#8 [emphasis mine]:
An id-表达式命名非类型模板参数类别类型T
表示类型的静态存储持续时间对象const T
,称为模板参数对象,其值是相应模板参数转换为类型后的值模板参数。程序中所有此类相同类型、相同值的模板参数都表示同一个模板参数对象。 [...]模板参数对象应不断销毁。
and, P0784R7 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0784r7.html,也被 C++20 接受,特别包含 constexpr 销毁的引入,包括更新类型为文字类型的要求;在论文的早期版本中特别描述,P0784R1 http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0784r1.html:
constexpr 析构函数的建议规则是:
- [...]
- 文字类型需要 constexpr 析构函数(之前,对平凡析构函数提出了更强的要求)