C++ 为什么RVO和NRVO不是本标准的强制性要求?

C++ 为什么RVO和NRVO不是本标准的强制性要求?,c++,copy-elision,C++,Copy Elision,为什么RVO和NRVO优化在标准中不是强制性的(适用时)?e、 g.函数生成某个对象并将其作为结果返回是一种非常常见的情况。由于RVO/NRVO,复制/移动构造函数通常被省略,但它们仍然需要定义,这有点令人困惑。如果RVO/NRVO在标准中,在这种情况下将不再需要复制/移动构造函数。标准不要求复制省略,因为这将要求所有实现在所有情况下都实现它 看看返回值优化和命名返回值优化的情况。简单地说: std::string Func() { return std::string("foo"); }

为什么RVO和NRVO优化在标准中不是强制性的(适用时)?e、 g.函数生成某个对象并将其作为结果返回是一种非常常见的情况。由于RVO/NRVO,复制/移动构造函数通常被省略,但它们仍然需要定义,这有点令人困惑。如果RVO/NRVO在标准中,在这种情况下将不再需要复制/移动构造函数。

标准不要求复制省略,因为这将要求所有实现在所有情况下都实现它

看看返回值优化和命名返回值优化的情况。简单地说:

std::string Func()
{
  return std::string("foo");
}
在功能相同的代码中:

后者比前者需要更多的编译器资源。不同的编译器在不同的情况下支持NRVO。当然,他们中的大多数人在这个小例子中都支持它,但是有很多不同的例子。在某些情况下,编译器只是说“去他妈的”,并不完全进行优化

您的方式需要以下选项之一:

  • 在所有适用的情况下强制执行复制省略,无论编译器实现起来有多困难。因此,现在每个编译器编写者都必须处理这样的情况:

    std::string Func(bool b)
    {
      if(b)
      {
        std::string named("foo");
        return named;
      }
      else
      {
        std::string named("bar");
        return named;
      }
    }
    
    在这些情况下,许多编译器不处理NRVO。这是一个简单的例子;他们可能会变得更复杂

  • 仔细检查每一个编译器,找出总是使用复制省略的常见情况子集,然后在标准中指定它们作为需求。这完全是荒唐可笑的;您将根据实现细节进行标准化。这从来都不是一件好事


  • 请注意,在特定情况下,C++17可能会得到一个。也就是说,每当使用临时对象初始化同一类型的对象时,复制/移动都需要省略。这样就可以从函数返回一个不可移动的对象。

    RVO/NRVO是否可以替换所有复制/移动操作?消除复制/移动会破坏现有代码吗?如果无法干净地删除复制/移动,为什么。。哦,虫子们。据我所知,(N)RVO属于“似乎”规则。我相信就标准而言,这是有道理的。而且,不,这不允许您完全摆脱复制/移动构造函数,因为它们在许多其他上下文中使用。RVO/NRVO是标准允许的,因此由编译器来执行或不执行。我在问为什么在适用的情况下并不总是需要它。这将减少歧义,这是一件好事。移动语义可能在调试构建中起作用,但RVO不起作用。另外,移动语义解决了完美的转发问题。@syam:我不认为它属于“好像”规则,因为它会影响构造函数是否被实际调用(正确的程序可以通过向构造函数添加调试输出来检测优化是否存在)。相反,这是一个特别允许的优化。在我看来,当指定的返回值中没有歧义时(即返回的变量可以静态确定),执行NRVO的规则就足够了,所以我认为没有太多的实现细节。我没有看到任何纯RVO不能执行的情况。“当命名返回值中没有歧义时”,那么在这种情况下可以执行的编译器呢?您仍然需要规范中当前的复制省略语言,以便这些编译器可以优化这些情况。在这一点上,你得到了宝贵的一点。您已经使规范更加复杂,以获得您可能已经拥有的优化。编译器中的任何内容都没有任何改进。所以重点是什么?当编译器无法确定将返回哪个局部变量时,它如何执行NRVO?对我来说似乎是不可能的,至少GCC4.8.1不能做到这一点,正如我从运行您的测试中看到的。其他编译器可以做到这一点吗?指定的返回值是否可以静态确定将因编译器而异。例如,内联可能会将非常量条件变为常量,此时编译器可能会确定其中一个返回语句不可访问,并将NRVO应用于另一个返回语句。
    std::string Func(bool b)
    {
      if(b)
      {
        std::string named("foo");
        return named;
      }
      else
      {
        std::string named("bar");
        return named;
      }
    }