3

(这个问题的灵感来自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();
}

以及它在 GodBolt 上的编译结果

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

  • 是否允许编译器简单地s_从 ctor 的参数构造,而不是构造s
  • 如果不是,编译器是否允许从 移动构造s_s看看后者是如何被使用的?
  • 如果前面的任何一个答案是“是” - 为什么 gcc 和 clang 不这样做?
  • 如果s构造正确,编译器不能避免构造hello,看看它没有其他用途吗?或者至少摆脱它?
4

1 回答 1

9

在as-if 下,我敢肯定你所要求的大部分都可以完成,假设你去链接时间并且你bar做空并且永远不会在任何地方覆盖新的。

但是,在as-if 下,您的程序是一个空程序,它没有可观察到的效果。

不允许编译器根据抽象机的规则移动s_构造s。如果你想让它被移动构造,std::move它。

可以将左值视为右值的情况是有限且特定的,并且涉及return x;语句。这不是一个return x;声明。

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

不允许编译器s进入s_. 有一些提议允许更激进的省略规则。

但到目前为止,仅在 as-if、带有纯右值或带有return x;语句的情况下才允许省略。好像消除真的很难用分配这样复杂的东西来证明,大多数编译器都不会尝试。这在目标文件生成时是不可能的,因为有人可以替换全局分配器。

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

exit或在 2 次分配后调用的全局分配器。产生的抽象机器不应该调用bar();如果我们消除您的额外对象,则该程序不会按照标准要求运行。

于 2018-02-07T14:36:31.010 回答