c++ - 空默认构造函数与隐式定义的不同机器代码
问题描述
鉴于以下结构...
#include <type_traits>
struct C {
long a[16]{};
long b[16]{};
C() = default;
};
// For godbolt
C construct() {
static_assert(not std::is_trivial_v<C>);
static_assert(std::is_standard_layout_v<C>);
C c;
return c;
}
...gcc(x86-64 Linux 上的 10.2 版)启用了优化(在所有 3 个级别)为:生成以下程序集[1]construct
:
construct():
mov r8, rdi
xor eax, eax
mov ecx, 32
rep stosq
mov rax, r8
ret
一旦我提供空的默认构造函数......
#include <type_traits>
struct C {
long a[16]{};
long b[16]{};
C() {} // <-- The only change
};
// For godbolt
C construct() {
static_assert(not std::is_trivial_v<C>);
static_assert(std::is_standard_layout_v<C>);
C c;
return c;
}
...生成的程序集更改为单独初始化每个字段,而不是原始中的单个 memset:
construct():
mov rdx, rdi
mov eax, 0
mov ecx, 16
rep stosq
lea rdi, [rdx+128]
mov ecx, 16
rep stosq
mov rax, rdx
ret
显然,这两个结构都不是微不足道的,而是标准布局。只是 gcc 错过了优化机会,还是从 C++ 语言的角度来看还有更多?
该示例是生产代码的精简版本,其中在性能上确实存在重大差异。
解决方案
虽然我同意这似乎是一个错失的优化机会,但我注意到语言层面的一个不同之处。隐式定义的构造函数是constexpr
,而您的示例中的空默认构造函数不是。来自cppreference.com:
也就是说,[隐式定义的构造函数]调用此类的基类和非静态成员的默认构造函数。如果这满足 constexpr 构造函数的要求,则生成的构造函数是 constexpr (C++11 起)。
long
因此,作为is的数组的初始化constexpr
,隐式定义的构造函数也是。但是,用户定义的不是,因为它没有标记constexpr
。我们也可以通过尝试制作construct
示例的功能来确认这一点constexpr
。对于隐式定义的构造函数,这没有任何问题,但对于空的用户定义版本,它无法编译,因为
<source>:3:8: 注意:'C' 不是聚合,没有普通的默认构造函数,也没有不是复制或移动构造函数的 'constexpr' 构造函数
正如我们在这里看到的:https ://godbolt.org/z/MnsbzKv1v
因此,为了解决这种差异,我们可以创建空的用户定义构造函数constexpr
:
struct C {
long a[16]{};
long b[16]{};
constexpr C() {}
};
有点令人惊讶的是,gcc 现在生成了优化版本,即与默认构造函数完全相同的代码:https ://godbolt.org/z/cchTnEhKW
我不知道为什么,但constexpr
在这种情况下,这种差异实际上似乎对编译器有所帮助。所以虽然看起来 gcc 应该能够在不指定的情况下生成相同的代码constexpr
,但我想知道它可能是有益的是件好事。
作为对这一观察的额外测试,我们可以尝试使隐式定义的构造函数变为 non-constexpr
并查看 gcc 是否无法进行优化。我能想到的一种尝试测试的简单方法是C
从具有非constexpr
默认构造函数的空类继承:
struct D {
D() {}
};
struct C : D {
long a[16]{};
long b[16]{};
C() = default;
};
实际上,这会生成再次单独初始化字段的程序集。一旦我们 make D()
constexpr
,我们就会得到优化的代码。请参阅https://godbolt.org/z/esYhc1cfW。
推荐阅读
- php - 我想使用 php 验证一个由标点符号组成的句子?
- nearprotocol - 向 NFT 元数据添加字段
- c++ - std::regex, [:print:] graphical characters
- python-3.x - Augment a Python enum with a value taking parameters
- apache-spark - Trino create table with special characters in nested columns
- css - 声明全局 CSS 变量会在 Dreamweaver 中生成错误
- python - 终端输入阻止了python中的多处理
- r - 如何在 R 中为特定 id 添加随机观察
- yolov5 - 经过训练的 yolov5s.pt 的大小比预期的要大得多
- android - 字体系列时Android Chrome缺少波兰语符号:衬线