提问人:Neil G 提问时间:7/13/2009 最后编辑:AustinWBryanNeil G 更新时间:11/18/2023 访问量:99692
通过右值引用返回是否更有效?
Is returning by rvalue reference more efficient?
答:
Beta_ab&&
Beta::toAB() const {
return move(Beta_ab(1, 1));
}
这将返回一个悬空引用,就像左值引用案例一样。函数返回后,临时对象将被销毁。应按值返回,如下所示Beta_ab
Beta_ab
Beta::toAB() const {
return Beta_ab(1, 1);
}
现在,它正确地将临时对象移动到函数的返回值中。如果编译器可以,它将通过使用 RVO(返回值优化)完全避免移动。现在,您可以执行以下操作Beta_ab
Beta_ab ab = others.toAB();
它会将临时构造移动到 中,或者执行 RVO 以完全省略移动或复制。我建议您阅读 BoostCon09 Rvalue References 101,它解释了这个问题,以及 (N)RVO 如何与此相互作用。ab
在其他情况下,返回右值引用的情况将是一个好主意。想象一下,你有一个函数,你经常在临时函数上调用它。让它返回右值临时值的常量左值引用并不是最佳选择。你可以像这样实现它getAB()
struct Beta {
Beta_ab ab;
Beta_ab const& getAB() const& { return ab; }
Beta_ab && getAB() && { return move(ab); }
};
请注意,在这种情况下不是可选的,因为既不是本地自动值,也不是临时值。现在,ref-qualifier 表示在 rvalue 临时函数上调用第二个函数,进行以下移动,而不是复制move
ab
&&
Beta_ab ab = Beta().getAB();
评论
例如,在稍微不同的上下文中,它可以更有效:
template <typename T>
T&& min_(T&& a, T &&b) {
return std::move(a < b? a: b);
}
int main() {
const std::string s = min_(std::string("A"), std::string("B"));
fprintf(stderr, "min: %s\n", s.c_str());
return 0;
}
有趣的是,在我的机器上,为上述代码生成了 54 条指令,而为常规代码生成了 62 条指令。但是,它为上述代码生成 518 条指令,而常规代码生成 481 条指令。clang++ -O3
std::min
-O0
std::min
更新
对于不同意或不理解这个答案的人,我建议先在编译器资源管理器(又名 godbolt (https://godbolt.org))中玩这个示例,然后再说有问题:
// use compiler flags: -O3 --std=c++20 -fno-exceptions
#include <utility>
#include <string>
// Example of the `min(a, b)` implementation that can take advantage of return by rvalue.
template <typename T>
T&& min_(T&& a, T &&b) {
return std::move(a < b? a: b);
}
// Test class that supports both copy and move construction.
struct Bar {
Bar();
Bar(const Bar &other);
Bar(Bar &&other);
~Bar();
char *ptr;
friend bool operator <(const Bar& lhs, const Bar& rhs);
};
// Just to tell the optimizer that we care about the value.
void consume(const void *);
#if 1
void with_rvalue() {
const Bar s = min_(Bar(), Bar());
consume(&s);
}
#else
void without_rvalue() {
const Bar s = std::min(Bar(), Bar());
consume(&s);
}
#endif
我们得到:with_rvalue()
Bar::Bar() [object constructor]
Bar::Bar() [object constructor]
operator<(Bar const&, Bar const&)
Bar::Bar(Bar&&) [move object constructor]
Bar::~Bar() [object destructor]
Bar::~Bar() [object destructor]
consume(void const*)
Bar::~Bar() [object destructor]
我们得到:without_rvalue()
Bar::Bar() [object constructor]
Bar::Bar() [object constructor]
operator<(Bar const&, Bar const&)
Bar::Bar(Bar const&) [copy object constructor]
Bar::~Bar() [object destructor]
Bar::~Bar() [object destructor]
consume(void const*)
Bar::~Bar() [object destructor]
正如你所看到的,当按 rvalue 返回时,我们避免了对象复制。这个原语的实用性是另一个主题,但这里有一个示例,说明按右值返回如何生成更优化的代码。
评论
std::move()
上一个:通过右值引用返回是否更有效?
下一个:对无符号字节进行饱和减法/加法
评论