jdi*_*jdi 5 c++ const reference
我遇到了一个C++习惯,我试图研究它以了解它的影响并验证它的用法.但我似乎无法找到确切的答案.
std::vector< Thing > getThings();
void do() {
const std::vector< Thing > &things = getThings();
}
Run Code Online (Sandbox Code Playgroud)
这里我们有一些返回非const&值的函数.我看到的习惯是在const&从函数中分配返回值时使用左值.提出这种习惯的推理是它减少了副本.
现在我一直在研究RVO(返回值优化),复制省略和C++ 11移动语义.我意识到,无论在何处使用,给定的编译器都可以选择通过RVO来防止复制const&.但是,在防止复制方面,const&左值的使用是否会对非const&返回值产生任何影响?在移动语义之前,我特别询问了前C++ 11编译器.
我的假设是编译器实现RVO或不实现RVO,并且说左值应该const&不提示或强制无副本情况.
编辑
我特别询问const&这里的用法是否减少了副本,而不是临时对象的生命周期,如"最重要的const"所述
进一步澄清问题
这是:
const std::vector< Thing > &things = getThings();
Run Code Online (Sandbox Code Playgroud)
与此不同的是:
std::vector< Thing > things = getThings();
Run Code Online (Sandbox Code Playgroud)
在减少副本方面?或者它对编译器是否可以减少副本(例如通过RVO)没有任何影响?
嘿,所以你的问题是:
\n\n“当函数按值返回类实例,并将其分配给 const 引用时,是否可以避免复制构造函数调用?”
\n\n忽略临时的生命周期,因为这不是您要问的问题,我们可以通过查看程序集输出来了解会发生什么。I\xe2\x80\x99m 使用 clang,llvm 7.0.2。
\n\n这里\xe2\x80\x99s是盒子标准的东西。按价值回报,没什么花哨的。
\n\n测试A
\n\nclass MyClass\n{\npublic:\n MyClass();\n MyClass(const MyClass & source);\n long int m_tmp;\n};\n\nMyClass createMyClass();\n\nint main()\n{\n const MyClass myClass = createMyClass();\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如果我使用 \xe2\x80\x9c-O0 -S -fno-elide-constructors\xe2\x80\x9d 进行编译,我会得到这个。
\n\n_main:\n pushq %rbp # Boiler plate\n movq %rsp, %rbp # Boiler plate\n subq $32, %rsp # Reserve 32 bytes for stack frame\n leaq -24(%rbp), %rdi # arg0 = &___temp_items = rdi = rbp-24\n movl $0, -4(%rbp) # rbp-4 = 0, no idea why this happens\n callq __Z13createMyClassv # createMyClass(arg0)\n leaq -16(%rbp), %rdi # arg0 = & myClass\n leaq -24(%rbp), %rsi # arg1 = &__temp_items\n callq __ZN7MyClassC1ERKS_ # MyClass::MyClass(arg0, arg1)\n xorl %eax, %eax # eax = 0, the return value for main\n addq $32, %rsp # Pop stack frame\n popq %rbp # Boiler plate\n retq\nRun Code Online (Sandbox Code Playgroud)\n\n我们只查看调用代码。我们\xe2\x80\x99对createMyClass的实现不感兴趣。\xe2\x80\x99s 在其他地方编译。\n因此 createMyClass 在临时文件中创建该类,然后将其复制到 myClass 中。
\n\n简单。
\n\nconst ref 版本怎么样?
\n\n测试B
\n\nclass MyClass\n{\npublic:\n MyClass();\n MyClass(const MyClass & source);\n long int m_tmp;\n};\n\nMyClass createMyClass();\n\nint main()\n{\n const MyClass & myClass = createMyClass();\n return 0;\n}\nRun Code Online (Sandbox Code Playgroud)\n\n相同的编译器选项。
\n\n_main: # Boiler plate\n pushq %rbp # Boiler plate\n movq %rsp, %rbp # Boiler plate\n subq $32, %rsp # Reserve 32 bytes for the stack frame\n leaq -24(%rbp), %rdi # arg0 = &___temp_items = rdi = rbp-24\n movl $0, -4(%rbp) # *(rbp-4) = 0, no idea what this is for\n callq __Z13createMyClassv # createMyClass(arg0)\n xorl %eax, %eax # eax = 0, the return value for main\n leaq -24(%rbp), %rdi # rdi = &___temp_items\n movq %rdi, -16(%rbp) # &myClass = rdi = &___temp_items;\n addq $32, %rsp # Pop stack frame\n popq %rbp # Boiler plate\n retq\nRun Code Online (Sandbox Code Playgroud)\n\n没有复制构造函数,因此更优化,对吧?
\n\n如果我们关闭两个版本的 \xe2\x80\x9c-fno-elide-constructors\xe2\x80\x9d 会发生什么?仍然保留-O0。
\n\n测试A
\n\n_main:\n pushq %rbp # Boiler plate\n movq %rsp, %rbp # Boiler plate\n subq $16, %rsp # Reserve 16 bytes for the stack frame\n leaq -16(%rbp), %rdi # arg0 = &myClass = rdi = rbp-16\n movl $0, -4(%rbp) # rbp-4 = 0, no idea what this is\n callq __Z13createMyClassv # createMyClass(arg0)\n xorl %eax, %eax # eax = 0, return value for main\n addq $16, %rsp # Pop stack frame\n popq %rbp # Boiler plate\n retq\nRun Code Online (Sandbox Code Playgroud)\n\nClang 删除了复制构造函数调用。
\n\n测试B
\n\n_main: # Boiler plate\n pushq %rbp # Boiler plate\n movq %rsp, %rbp # Boiler plate\n subq $32, %rsp # Reserve 32 bytes for the stack frame\n leaq -24(%rbp), %rdi # arg0 = &___temp_items = rdi = rbp-24\n movl $0, -4(%rbp) # rbp-4 = 0, no idea what this is\n callq __Z13createMyClassv # createMyClass(arg0)\n xorl %eax, %eax # eax = 0, return value for main\n leaq -24(%rbp), %rdi # rdi = &__temp_items\n movq %rdi, -16(%rbp) # &myClass = rdi\n addq $32, %rsp # Pop stack frame\n popq %rbp # Boiler plate\n retq\nRun Code Online (Sandbox Code Playgroud)\n\n测试 B(分配给 const 引用)与之前相同。它现在比测试 A 有更多的指令。
\n\n如果我们将优化设置为 -O1 会怎样?
\n\n_main:\n pushq %rbp # Boiler plate\n movq %rsp, %rbp # Boiler plate\n subq $16, %rsp # Reserve 16 bytes for the stack frame\n leaq -8(%rbp), %rdi # arg0 = &___temp_items = rdi = rbp-8\n callq __Z13createMyClassv # createMyClass(arg0)\n xorl %eax, %eax # ex = 0, return value for main\n addq $16, %rsp # Pop stack frame\n popq %rbp # Boiler plate\n retq\nRun Code Online (Sandbox Code Playgroud)\n\n当使用 -O1 编译时,两个源文件都会变成这样。\n它们生成完全相同的汇编程序。\n对于 -O4 也是如此。
\n\n编译器不知道 createMyClass 的内容,因此它无法执行更多优化操作。
\n\n使用我正在使用的编译器,分配给 const 引用不会带来任何性能提升。
\n\n我想 g++ 和 intel 的情况类似,尽管检查总是好的。
\n