为什么标准不强制要求实现RVO和NRVO?

7

为什么标准没有强制要求使用RVO和NRVO优化(当它们适用)?例如,一个函数通常会生成一些对象并将其作为结果返回。由于RVO/NRVO,复制/移动构造函数通常会被省略,但是它们仍然需要被定义,这有点令人困惑。如果RVO/NRVO被纳入标准,那么在这种情况下就不再需要复制/移动构造函数了。


RVO/NRVO能否替代所有的复制/移动操作?消除复制/移动会破坏现有代码吗?如果无法干净地删除复制/移动,为什么..哦,蠕虫。 - user2246674
据我所知,(N)RVO算作“仿佛”规则。我相信它在标准上是有意义的。而且,,这并不能完全摆脱复制/移动构造函数,因为它们在很多其他情况下都会被使用到。 - syam
2
RVO/NRVO是标准允许的,所以是否执行取决于编译器。我想知道为什么在适用情况下它并不总是必需的。这将减少歧义,这是一件好事。 - lizarisk
当然,我是指在这种特定情况下不需要复制/移动构造函数。 - lizarisk
3
我不认为它属于“仿佛”规则,因为它会影响构造函数是否被调用(通过在构造函数中添加调试输出,正确的程序可以检测到优化的存在或缺失)。相反,它是一种明确允许的优化。 - ruakh
显示剩余5条评论
1个回答

2
“复制省略”不是标准要求的,因为这将要求所有实现在所有情况下都实现它。
只需看一下返回值优化与“命名”返回值优化的情况。仅仅把这个转换:
std::string Func()
{
  return std::string("foo");
}

转化成这段“功能完全相同”的代码:
std::string Func()
{
  std::string named("foo");
  return named;
}

后者对编译器的要求比前者高得多。不同的编译器在不同的情况下支持NRVO。当然,大多数编译器都支持这种微不足道的情况,但是还有很多不同的情况。而且有些情况下,编译器干脆放弃了优化。
您的方式将需要以下之一:
  1. To enforce copy elision in all applicable cases, no matter how difficult to implement for compilers. So now every compiler writer has to deal with the cases like this:

    std::string Func(bool b)
    {
      if(b)
      {
        std::string named("foo");
        return named;
      }
      else
      {
        std::string named("bar");
        return named;
      }
    }
    

    Many compilers don't handle NRVO in those cases. And that's a simple case; they can get much more complex than that.

  2. Go through every compiler and find a common subset of cases where copy elision is always used, then specify them in the standard as requirements. That's utterly ludicrous; you'd be standardizing based on implementation details. That's never a good thing.

请注意,C++17 可能会在特定情况下获得复制省略保证。即,每当使用临时对象初始化相同类型的对象时,都需要省略复制/移动操作。这使得从函数返回不可移动对象成为可能。

在我看来,当命名返回值中没有歧义时(即可以静态确定返回变量),执行NRVO规则将足够,因此我认为这并不涉及太多实现细节。而且我没有看到任何无法执行纯RVO的情况。 - lizarisk
当命名返回值没有歧义时,*当有编译器可以在这些情况下执行时呢?你仍然需要规范中的当前复制省略语言,以便这些编译器可以优化这些情况。此时,你几乎没有获得任何东西。你已经让规范更加复杂,只是为了获得一个你可能已经拥有的优化。你的编译器没有任何改进。那么...这有什么意义呢? - Nicol Bolas
编译器如何执行NRVO,当它无法确定哪个本地变量将被返回?对我来说似乎是不可能的,至少GCC 4.8.1不能做到这一点,正如我从运行您的测试中看到的那样。还有哪些编译器可以做到这一点? - lizarisk
@lizarisk 命名返回值是否可以在静态上确定,将因编译器而异。例如,内联可能会将非常量条件变为常量,在这种情况下,编译器可能会确定其中一个返回语句是不可达的,并将 NRVO 应用于另一个返回语句。 - user743382

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接