c++ - `default` 构造函数在常量表达式中具有未初始化的成员
问题描述
以下最小示例因未初始化数组数据成员而被 Clang 和 GCC 拒绝:
class vector3
{
public:
constexpr vector3() = default;
private:
float m_data[3];
};
constexpr auto vec = vector3{};
这会产生相当直接的错误:
<source>:4:15: error: explicitly defaulted function 'constexpr vector3::vector3()' cannot be declared 'constexpr' because the implicit declaration is not 'constexpr':
4 | constexpr vector3() = default;
| ^~~~~~~
<source>:6:11: note: defaulted default constructor does not initialize 'float vector3::m_data [3]'
6 | float m_data[3];
| ^~~~~~
上面代码的目的是确保vector3
可以通过值初始化(例如vector3{}
)在常量表达式中使用,这将对子元素(m_data
)进行零初始化。
错误是由于使用了constexpr
关键字而发生的,解决方法是简单地删除关键字并允许default
正确推断这是否可以在常量表达式中使用:
class vector3
{
public:
vector3() = default;
private:
float m_data[3];
};
constexpr auto vec = vector3{}; // now works?
奇怪的是,这实际上现在可以工作了——并且仍然能够产生一个常量表达式,m_data
初始化为零,在 GCC 的程序集中可见(类似的存在于 Clang 中,但带有 XOR 指令):
vec:
.zero 12
我的问题是:怎么可能= default
产生一个(有效的)constexpr
构造函数,而constexpr ... = default
由于它无效而失败constexpr
?
这个问题似乎会影响 C++20 之前的 C++ 版本(C++11 到 C++17)。这在 C++20 中有所改变吗?
解决方案
是的,确实在 C++20 中,规则已更改,因此constexpr
不再需要构造函数来初始化所有非静态成员和基类子对象。
在 C++20 之前,我们有一个有趣的情况,即你的构造函数不能被声明constexpr
,但是该vector3
类型的对象仍然可以在常量表达式中使用,因为在其第一次声明时显式默认的默认构造函数实际上并没有在值期间被调用- 初始化,除非它是非平凡的 (C++17 [dcl.init]/8.2),因此constexpr
不会触发禁止在常量表达式中调用非函数。这不是编译器错误;这只是语言的一个怪癖。
推荐阅读
- ios - 如何在同一订阅组中使用 Apple In-App Purchase 对订阅计划进行排名?
- java - 如何使用 Apache Jena 的查询构建器向 SelectBuilder 添加 SERVICE 子句?
- javascript - 为什么“这个”会丢失?
- javascript - 如何根据复选框/框状态更改 URL 正文?
- google-bigquery - GROUP BY 使用 Ads Data Hub (BigQuery) 中的参数
- pine-script - 为什么当蜡烛颜色改变时这个脚本(pinescript)没有退出?
- php - Laravel 8:雄辩的查询似乎显示从数据库中检索到的数据
- sql - 从 INNER JOIN 向 MIN 和 MAX 语句添加其他列
- react-typescript - 如何在反应中为组件方法编写单元测试用例
- sql - 每次两个表值的总和,并在 SQL-Server 中找到总和结果的最小值