如何避免复制构造返回值 [英] How to avoid copy-constructing a return value

查看:61
本文介绍了如何避免复制构造返回值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 C++ 的新手,最近在返回对局部变量的引用时遇到了问题.我通过将返回值从 std::string& 更改为 std::string 来解决它.但是,据我所知,这可能非常低效.考虑以下代码:

I'm a newcomer to C++ and I ran into a problem recently returning a reference to a local variable. I solved it by changing the return value from std::string& to an std::string. However, to my understanding this can be very inefficient. Consider the following code:

string hello()
{
    string result = "hello";
    return result;
}

int main()
{
    string greeting = hello();
}

据我所知,会发生什么:

To my understanding, what happens is:

  • hello() 被调用.
  • 局部变量result被赋值为"hello".
  • result的值复制到变量greeting中.
  • hello() is called.
  • Local variable result is assigned a value of "hello".
  • The value of result is copied into the variable greeting.

这对于 std::string 来说可能没有那么重要,但如果你有一个包含数百个条目的哈希表,它肯定会变得昂贵.

This probably doesn't matter that much for std::string, but it can definitely get expensive if you have, for example, a hash table with hundreds of entries.

如何避免复制构造返回的临时对象,而是返回指向对象的指针的副本(本质上是局部变量的副本)?

How do you avoid copy-constructing a returned temporary, and instead return a copy of the pointer to the object (essentially, a copy of the local variable)?

旁注:我听说编译器有时会执行返回值优化以避免调用复制构造函数,但我认为最好不要依赖编译器优化来使您的代码高效运行.)

Sidenote: I've heard that the compiler will sometimes perform return-value optimization to avoid calling the copy constructor, but I think it's best not to rely on compiler optimizations to make your code run efficiently.)

推荐答案

您问题中的描述非常正确.但重要的是要了解这是抽象 C++ 机器的行为.事实上,抽象返回行为的规范描述更不理想

The description in your question is pretty much correct. But it is important to understand that this is behavior of the abstract C++ machine. In fact, the canonical description of abstract return behavior is even less optimal

  1. result 被复制到 std::string 类型的无名中间临时对象中.在函数返回后,这个临时仍然存在.
  2. 在函数返回后,那个无名的中间临时对象会被复制到greeting.
  1. result is copied into a nameless intermediate temporary object of type std::string. That temporary persists after the function's return.
  2. That nameless intermediate temporary object is then copied to greeting after function returns.

大多数编译器总是足够聪明,可以完全按照经典的复制省略规则消除中间临时文件.但即使没有那个中间临时行为,这种行为也一直被视为非常次优.这就是为什么给予编译器很大的自由,以便在按值返回的上下文中为它们提供优化机会.最初是返回值优化 (RVO).后来命名的返回值优化被添加到其中(NRVO).最后,在 C++11 中,移动语义成为在这种情况下优化返回行为的另一种方式.

Most compilers have always been smart enough to eliminate that intermediate temporary in full accordance with the classic copy elision rules. But even without that intermediate temporary the behavior has always been seen as grossly suboptimal. Which is why a lot of freedom was given to compilers in order to provide them with optimization opportunities in return-by-value contexts. Originally it was Return Value Optimization (RVO). Later Named Return Value Optimization was added to it (NRVO). And finally, in C++11, move semantics became an additional way to optimize the return behavior in such cases.

请注意,在您的示例中,在 NRVO 下,使用 "hello" 初始化 result 实际上将 "hello" 直接放入 问候从一开始.

Note that under NRVO in your example the initialization of result with "hello" actually places that "hello" directly into greeting from the very beginning.

所以在现代 C++ 中,最好的建议是:保持原样,不要避免它.按值返回.(并且尽可能在声明点使用立即初始化,而不是选择默认初始化然后赋值.)

So in modern C++ the best advice is: leave it as is and don't avoid it. Return it by value. (And prefer to use immediate initialization at the point of declaration whenever you can, instead of opting for default initialization followed by assignment.)

首先,编译器的 RVO/NRVO 功能可以(并且将会)消除复制.在任何自尊的编译器中,RVO/NRVO 都不是晦涩的或次要的.这是编译器编写者积极努力实现和正确实现的东西.

Firstly, the compiler's RVO/NRVO capabilities can (and will) eliminate the copying. In any self-respecting compiler RVO/NRVO is not something obscure or secondary. It is something compiler writers do actively strive to implement and implement properly.

其次,如果 RVO/NRVO 以某种方式失败或不适用,总是有移动语义作为后备解决方案.移动自然适用于按值返回的上下文,并且它比非平凡对象的全面复制要便宜得多.而 std::string 是一个可移动的类型.

Secondly, there's always move semantics as a fallback solution if RVO/NRVO somehow fails or is not applicable. Moving is naturally applicable in return-by-value contexts and it is much less expensive than full-blown copying for non-trivial objects. And std::string is a movable type.

这篇关于如何避免复制构造返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆