函数需要向调用者返回两个值。实施的最佳方式是什么?
选项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或任何其他编译器?答案 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)
如果在创建一对之后需要在u
和v
上做其他工作,我发现以下样式在C ++ 17中非常灵活:
pair<U,V> myfunc()
{
auto out = make_pair(getU(),getV());
auto& [u, v] = out;
// Work with u and v
return out;
}
对于编译器使用命名返回值优化,这应该是一个非常简单的情况