【问题标题】:Return Value Optimization and functions that build structures返回值优化和构建结构的函数
【发布时间】:2015-03-09 23:53:05
【问题描述】:

我有一个“构建”要返回的结构的函数:

struct stuff {
    int a;
    double b;
    Foo c;
};
stuff generate_stuff() {
    Foo c = generate_foo();
    //do stuff to Foo, that changes Foo:
    //...
    return {1, 2.0, c};  //should this be return {1, 2.0, move(c)};?
}

我应该将c 移出该功能吗?我意识到,(N)RVO 经常可以就地构建对象,但有时可能并非如此。什么时候不能完成 (N)RVO,因此,我应该什么时候移动函数的对象?

换句话说,这显然是返回的临时的 RVO。问题变成了,NRVO(命名返回值优化)会发生在c 上吗?将c就地构造(在函数的调用点,在临时的stuff结构内),还是将c在函数中构造,然后复制到调用点的结构中。

【问题讨论】:

  • 这构造了一个stuff 类型的临时文件:它将保存c 的副本(作为左值,c 不能移动到这个临时文件中)。然后这个临时本身可以被移出函数,并且它的Foo成员(本身是c的副本)将在那时被移动。
  • 如果Foo 包含内置类型,那么它不会产生影响,但假设移动构造Foo 比复制构造它更便宜,你应该使用std::move(c)。复制省略允许在该实例中省略 stuff 的复制/移动,但同样不适用于构成 stuff 的数据成员的构造。
  • RVO 仅适用于临时对象,如果要返回的表达式是名称,则 NRVO 仅适用于返回语句。因此,return {1, 2.0, generate_foo()};stuff s = {1, 2.0, generate_foo()}; /* do something */ return s; 都可以使用 (N)RVO。
  • @Praetorian:可以假设 Foo 不是 POD。 @IgorTandetnik:因此,stuff 类型的临时对象将在调用站点 (RVO) 构建,问题是 c 是否将构建到位,还是必须移动。
  • 不,这不是优化。标准要求return {..}; 直接初始化返回值。这与return 表达式 ; 不同,注意花括号初始化列表不是表达式。

标签: c++ c++11 move rvo


【解决方案1】:

您对move(c) 的调用不是将 c 移出函数,而是将其移入从函数返回的临时结构中。临时返回值应始终受益于 RVO。但是,我相信 c 到临时的移动/复制不能被优化掉,因为 c 本身不是临时的。所以这里的移动版本应该总是至少和复制版本一样高效(用 g++、clang++ 和 MVC++ 测试了一个简单的场景)。

如果您必须绝对最小化复制/移动操作的数量,那么您可以编写

struct stuff {
    int a;
    double b;
    Foo c;
};
stuff generate_stuff() {
    stuff s{ 1, 2.0, generate_foo() };
    //use s.c instead of c
    //...
    return s;  
}

由于 NRVO,这将导致仅 Foo 的单个构造并且没有复制/移动。

编辑: 正如@dyp 在您的问题的 cmets 中指出的那样,Stuff 的就地构造实际上并不是 RVO 的情况,而是标准所要求的。无论如何,重要的部分是c 的移动/复制不能被忽略,因此使用move 永远不会导致性能损失。

【讨论】:

  • @dyp:这与我所说的完全相反——或者我对英语的理解非常糟糕。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-12
相关资源
最近更新 更多