use*_*312 5 c++ move shared-ptr overhead-minimization
这是一个 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());
}
Run Code Online (Sandbox Code Playgroud)
问题归结为平台 ABI,并且通过完全不透明的类型可以更好地说明:
\nstruct A {\n A(const A&);\n A(A&&);\n ~A();\n};\n\nA make() noexcept;\nvoid take(A) noexcept;\n\nvoid foo() {\n take(make());\n}\nRun Code Online (Sandbox Code Playgroud)\n请参阅编译器资源管理器中的比较
\nvoid foo(void) PROC\n push ecx\n push ecx\n push esp\n call A make(void)\n add esp, 4\n call void take(A)\n add esp, 8\n ret 0\nvoid foo(void) ENDP\nRun Code Online (Sandbox Code Playgroud)\nfoo():\n sub rsp, 24\n lea rdi, [rsp+15]\n call make()\n lea rdi, [rsp+15]\n call take(A)\n lea rdi, [rsp+15]\n call A::~A() [complete object destructor]\n add rsp, 24\n ret\nRun Code Online (Sandbox Code Playgroud)\n\n\n如果该类型具有重要的析构函数,则调用者在控制权返回到该析构函数后调用该析构函数(包括调用者抛出异常时)。
\n
- Itanium C++ ABI \xc2\xa73.1.2.3 重要参数
\n这里发生的事情是:
\nmake()产生类型的纯右值Atake(A)这被输入到\nA只有 GCC 和 clang在调用站点相反,MSVC 会销毁被调用者内部的临时文件A(或者在您的情况下为std::shared_ptr),而不是在调用站点。您看到的额外代码是内联版本std::shared_ptr析构函数的内联版本。
最后,您应该不会看到任何重大的性能影响。但是,不幸的是,如果Func2重置/释放共享指针,则调用站点的大部分析构函数代码都会失效。此 ABI 问题类似于以下问题std::unique_ptr:
\n\n\n还有一个围绕函数参数的销毁顺序和析构函数的执行的语言问题
\nunique_ptr。为了简单起见,本文忽略了这一点,但“通过unique_ptr成本低廉T*”的完整解决方案也必须解决这个问题。
阿格纳·雾。-不同C++编译器和操作系统的调用约定
\n