为什么C++编译器不能在传值方案中优化更多字符串结构?

ein*_*ica 3 c++ stdstring compiler-optimization copy-elision stdmove

(这个问题的灵感来自Nicolai Josuttis的CppCon 2017演讲.)

考虑以下源文件(对于对象,而不是完整的程序):

#include <string>

class C {
    std::string s_;
public:
    C(std::string s) : s_(s) { };
    void bar();
};

void foo() {
    std::string hello { "The quick brown fox jumped over the lazy dog" };
    C c { hello };
    c.bar();
}
Run Code Online (Sandbox Code Playgroud)

它的编译结果在GodBolt上.

即使使用-O2(甚至使用-O3),似乎字符串构造函数被调用三次.具体而言,s构造,仅用于构造s_,然后被破坏.我的问题:

  • 是否允许编译器简单地s_从ctor的参数构造,而不是构造s?
  • 如果没有,则编译器允许移动,构建s_从s,看到后者是如何闲置?
  • 如果之前的任何答案都是"是" - 为什么gcc和clang不这样做?
  • 如果s构造正确,编译器不能避免构造hello,看看它没有其他用途吗?或者至少离开它?

Yak*_*ont 9

在假设的情况下,我确定你要求的大部分内容都可以完成,假设你去链接时间而你bar做空并且永远不会覆盖新的任何地方.

但是,根据假设,你的程序是一个空程序,它没有可观察到的影响.

编译器不允许移动构建体s_从s抽象机的规则下.如果你想要它被移动构造,std::move它.

左值可以被视为右值的情况是有限且具体的并涉及return x;陈述.这不是一个return x;声明.

所以你的代码必须复制s到s_.很可能它也应该产生警告作为实施质量问题.

编译器不允许s进入s_.已经有一些提案允许更加激进的省略规则.

但截至目前,elision只能在假设,prvalues或return x;声明下使用.如果消除确实非常难以用分配这么复杂的东西来证明,大多数编译器都不会尝试.并且在目标文件生成中是不可能的,因为有人可以替换全局分配器.

想象一下全局分配器覆盖,它打印出完成了多少分配.然后使用那些"从未使用过"的对象,因为他们应该打印出他们所做的分配.

或者是exit在2次分配后调用的全局分配器.由此产生的抽吸机应该永远不会打电话bar(); 如果我们消除你的额外对象,程序就不会像标准要求那样行事.