C++23 std::is_implicit_lifetime 返回 true 是 Bug 吗?
这里返回 true 符合 C++23 规则,给出的输出不能说明 clang/libc++ 有 Bug。TC_NotILT 虽然不是 aggregate,但它的复制、移动构造函数都是 trivial 且 eligible,析构函数也是 trivial 且未删除,因此仍属于 implicit-lifetime class。
为什么非 aggregate 也能满足条件?
C++23 对 implicit-lifetime class 给出了两条可选条件,满足其中一条即可:
- 它是 aggregate,且析构函数不是 user-provided。
- 它至少有一个 trivial eligible constructor,并且析构函数是 trivial、未删除的。
因此,“不是 aggregate”只能排除第一条,不能直接推出它不是 implicit-lifetime class。依据见 C++23 草案的 class.prop。
第二条中的 constructor 也包括复制构造函数和移动构造函数,不要求必须有 trivial 的默认构造函数。
TC_NotILT 的哪些构造函数符合要求?
用户提供的构造函数:
TC_NotILT(int i) : val{i} { std::cout << "not ILT\n"; }
这个构造函数不是 trivial,但不会妨碍编译器隐式声明复制和移动构造函数。这个类只有一个 int 成员,没有基类和虚函数;它隐式声明的下列构造函数都是 trivial,且满足 eligible 条件:
TC_NotILT(const TC_NotILT&);
TC_NotILT(TC_NotILT&&);
这两行只是列出隐式成员的签名,无须手动加入原类定义。复制、移动构造函数的隐式声明及 trivial 条件见 C++23 草案的 class.copy.ctor。
该类的隐式析构函数也是 trivial 且未删除,因此满足 implicit-lifetime class 的第二条条件。
如何修正预期并检查各项性质?
把原来的第二个输出提示改为 expecting true 即可。也可以在原类定义之后、main() 之外加入以下断言,检查相关性质:
static_assert(!std::is_aggregate_v<TC_NotILT>);
static_assert(std::is_trivially_copyable_v<TC_NotILT>);
static_assert(std::is_trivially_copy_constructible_v<TC_NotILT>);
static_assert(std::is_trivially_move_constructible_v<TC_NotILT>);
static_assert(std::is_trivially_destructible_v<TC_NotILT>);
static_assert(!std::is_default_constructible_v<TC_NotILT>);
static_assert(!std::is_trivially_constructible_v<TC_NotILT, int>);
static_assert(std::is_implicit_lifetime_v<TC_NotILT>);
这些断言表达了按标准规则应满足的结果。使用时需要启用 C++23,并使用提供 std::is_implicit_lifetime 的标准库。
原代码的注释还需要注意什么?
“所有特殊成员函数都会隐式声明”这一说法不够准确。由于已经声明了 TC_NotILT(int),编译器不会再隐式声明默认构造函数,因此不能无参构造 TC_NotILT。这符合 C++23 默认构造函数规则,也不影响前面的 implicit-lifetime 判断。
另外,类型特征查询不会构造 TC_NotILT 对象,因此原程序不会执行构造函数中的输出语句。是否属于 implicit-lifetime type,与正常调用 TC_NotILT(int) 时是否执行其函数体,是两个不同的问题。
备注:内容仅供参考。
