Noa*_*oah 5 c++ abi calling-convention micro-optimization compiler-optimization
举个简单的例子:
struct has_destruct_t {
int a;
~has_destruct_t() {}
};
struct no_destruct_t {
int a;
};
int bar_no_destruct(no_destruct_t);
int foo_no_destruct(void) {
no_destruct_t tmp{};
bar_no_destruct(tmp);
return 0;
}
int bar_has_destruct(has_destruct_t);
int foo_has_destruct(void) {
has_destruct_t tmp{};
bar_has_destruct(tmp);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
foo_has_destruct代码生成稍差一些,因为析构函数似乎强制tmp进入堆栈:
foo_no_destruct(): # @foo_no_destruct()
pushq %rax
xorl %edi, %edi
callq bar_no_destruct(no_destruct_t)@PLT
xorl %eax, %eax
popq %rcx
retq
foo_has_destruct(): # @foo_has_destruct()
pushq %rax
movl $0, 4(%rsp)
leaq 4(%rsp), %rdi
callq bar_has_destruct(has_destruct_t)@PLT
xorl %eax, %eax
popq %rcx
retq
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/388K1EfYa
但是,考虑到析构函数是 1)普通内联的并且 2)空的,为什么需要这样的情况呢?
有没有办法以零成本包含析构函数?
Itanium C++ ABI 调用约定定义具有非平凡析构函数的类型必须在堆栈上传递,并且~has_destruct_t() {}始终是非平凡析构函数。普通析构函数必须在其第一个声明 ( ~has_destruct_t() = default) 中隐式声明或默认。
如果纯右值作为函数参数传递,则该规则对于使复制省略起作用是必要的。复制省略要求函数参数的地址与从纯右值函数参数具体化的临时对象的地址相同,这是一个可观察的属性。
因此调用者需要为临时对象(同时也是函数参数对象)提供内存以确保地址相等。
自 C++17 起,对于具有非平凡(且不可删除)析构函数的类型,这种复制省略是强制性的,但在 Itanium C++ ABI 使用的所有以前的 C++ 标准版本中也允许这种复制省略。
如果您不想在析构函数体中执行任何操作,则根本不要声明它,除非您还需要 make 它virtual,在这种情况下virtual ~has_destruct_t() = default;就可以了。
| 归档时间: |
|
| 查看次数: |
152 次 |
| 最近记录: |