返回std :: pair时会发生RVO吗?

时间:2012-12-26 16:55:39

标签: c++ c++11 std-pair rvo nrvo

函数需要向调用者返回两个值。实施的最佳方式是什么?

选项1:

pair<U,V> myfunc()
{
...
return make_pair(getU(),getV());
}

pair<U,V> mypair = myfunc();

选项1.1:

// Same defn
U u; V v;
tie(u,v) = myfunc();

选项2:

void myfunc(U& u , V& v)
{
u = getU(); v= getV();
}

U u; V v;
myfunc(u,v);

我知道Option2,没有副本/动作,但看起来很难看。 Option1,1.1中是否会出现任何副本/移动?让我们假设U和V是支持复制/移动操作的巨大对象。

问:理论上,根据标准,任何RVO / NRVO优化理论上是否可行?如果是,是否已实现gcc或任何其他编译器?

4 个答案:

答案 0 :(得分:8)

  

返回std::pair时会发生RVO吗?

是的,可以。

  

保证会发生吗?

不,不是。


C ++ 11标准:第12.8 / 31节:

  

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使该对象的复制/移动构造函数和/或析构函数具有副作用。

复制省略不是保证功能。优化编译器只要能够就可以执行。 w.r.t std::pair没什么特别的。如果编译器足以检测到优化机会,它将会这样做。所以你的问题是编译器特定的,但同样的规则适用于std::pair和任何其他类。

答案 1 :(得分:4)

虽然RVO不能得到保证,但在C ++ 11中你已定义它的函数我认为必须至少移动 - 返回,所以我建议留下更清晰的定义而不是扭曲它接受输出变量(除非您有特定的使用政策。)

此外,即使此示例确实使用了RVO,您明确使用make_pair意味着您将始终至少有一个额外的对构造,从而进行移动操作。更改它以返回大括号初始化的表达式:

return { getU(), getV() };

答案 2 :(得分:1)

RVO或Copy elision依赖于编译器,所以如果你想拥有RVO并避免调用Copy构造函数,最好选择是使用指针。

在我们的产品中,我们使用指针和boost容器指针来避免Copy构造函数。这确实使性能提升了大约10%。

来到你的问题, 在选项1中,U和V的复制构造函数将不会被调用,因为您没有返回U或V但是返回std :: pair对象,因此它的复制构造函数将被调用,大多数编译器肯定会在这里使用RVO来避免这种情况。

由于 Niraj Rathi

答案 3 :(得分:1)

如果在创建一对之后需要在uv上做其他工作,我发现以下样式在C ++ 17中非常灵活:

pair<U,V> myfunc()
{
  auto out = make_pair(getU(),getV());
  auto& [u, v] = out;
  // Work with u and v
  return out;
}

对于编译器使用命名返回值优化,这应该是一个非常简单的情况