这是一个 C++ 片段。 Func1 生成一个共享对象,直接移至 Func2 中。我们认为Func3中不应该有开销。将此代码片段放入 Compiler Explorer 中,我们发现 MSVC 的代码比 clang 或 gcc 短 2-3 倍。为什么会这样,也可以使用 clang/gcc 获得更短的代码吗?
看起来 Func3 生成了异常处理代码来清理临时共享对象。
#include <memory>
std::shared_ptr<double> Func1();
void Func2 (std::shared_ptr<double> s);
void Func3()
{
Func2(Func1());
}
问题归结为平台 ABI,并且通过完全不透明的类型可以更好地说明:
struct A {
A(const A&);
A(A&&);
~A();
};
A make() noexcept;
void take(A) noexcept;
void foo() {
take(make());
}
参见编译器资源管理器中的比较
void foo(void) PROC
push ecx
push ecx
push esp
call A make(void)
add esp, 4
call void take(A)
add esp, 8
ret 0
void foo(void) ENDP
foo():
sub rsp, 24
lea rdi, [rsp+15]
call make()
lea rdi, [rsp+15]
call take(A)
lea rdi, [rsp+15]
call A::~A() [complete object destructor]
add rsp, 24
ret
如果该类型具有重要的析构函数,则调用者将在控制权返回到该析构函数后调用该析构函数(包括调用者抛出异常时)。
- Itanium C++ ABI §3.1.2.3 重要参数
这里发生的是:
make()
产生类型为 A
take(A)
的参数中
A
MSVC 相反会销毁临时
A
(或者在您的情况下,std::shared_ptr
在被调用者内部,而不是在调用站点。您看到的额外代码是 std::shared_ptr
析构函数的内联版本。
最终,您应该不会看到任何重大的性能影响。在一种情况下,破坏发生在调用站点,而在另一种情况下,破坏发生在被调用者内部。必须进行大量的工作,无论付出还是承担,只是在不同的地点进行。