c++ - 关于从函数返回大值和 move 的困惑

标签 c++ c++11 move-semantics rvalue-reference

我正在观看来自 C++ And Beyond 2011 的 Scott Mayers、Herb Sutter 和 Andrei Alexandrescu 之间的一个旧小组讨论。在讨论中,人们会得到关于 c++11(当时是 c++0x)特性的问题错了,Andrei 提到人们假设从函数返回大值时 move 语义不会涉及成本是错误的。他是这么说的

I will not design interfaces now to return big things by value, for the sheer reason that for all r-value references, there are going to be plenty of cases for which unnecessary copies are going to be created. And that shouldn't be forgotten.

I don't design and I don't condone designing interfaces that return big things by value, because some day someone is going to assign from it and assignment is going to be inefficient.

I don't think the cost of returning big things should be forgotten in the aftermath of victory of r-value references.

Herb 详细阐述了以下内容:

I agree with you, but they are two different cases, one is you are generating a new result which you know you are going to put somewhere, and that's where you pass in by a non const reference, and have an out parameter, that's what out parameters are for.

There are other cases where you have two inputs and you are going to make something new, and there it is return by value, not as opposed to the out parameter thing, but its return by value instead of doing the clunky workaround that is error prone today, of heap allocation just to return a pointer, just to avoid that extra copy.

这是怎么回事,我只是不明白这两个家伙的意思。 安德烈所说的“从中分配”的成本有何不同? Herb 的解释也在我脑海中闪过。谁能详细说一下?

还请考虑以下代码:

vector<BigData> GetVector(int someIndex)
{
   vector<BigData> toFill;
   // some processing
   // filling the vector
   return toFill;
}

我认为 move 语义将使上面的代码等效于将空 vector 作为输出参数传递。不是这样吗?

这是 video 的链接.以上几点是在41分钟左右的播放时间后得出的。

最佳答案

我无法读懂 Herb、Andrei 或 Scott 的想法。 (除此之外:这些人——都非常有才华——与 Apollo 太空计划(他们视频中的背景)没有任何关系)。不过,我可以对右值引用/move 语义添加一些见解。

如果你有一个像这样的纯工厂函数:

vector<BigData>
GetVector(int someIndex)
{
   vector<BigData> toFill;
   // some processing
   // filling the vector
   return toFill;
}

那么一定要按值返回。今天的每个编译器都会执行 RVO,这意味着从 GetVector move/返回的成本完全为零。

话虽这么说,但在一种情况下,这是个糟糕的建议。对于诸如 std::stringstd::vector 之类的容器,它们具有 capacity() 的概念,或一些其他可提高性能的资源在不影响值(value)的情况下,有些例子表明您不想无偿丢弃该资源。

例如,考虑:

vector<BigData> data;
while (I_need_to)
{
    data = GetVector(someIndex);
    process(data);
}

在这个例子中,每次通过循环,GetVector 都会分配一个新的 vector,这可能是一种浪费,因为 vector 有可以在这样的循环中重复使用的容量。考虑这个重写:

void
GetVector(int someIndex, vector<BigData>& toFill)
{
   toFill.clear();
   // some processing
   // filling the vector
   return toFill;
}
// ...
vector<BigData> data;
while (I_need_to)
{
    GetVector(someIndex, data);
    process(data);
}

在这个重写中,data 仍然在每次循环中得到一个新值。但不同的是,前一个循环的 capacity() 被保存下来,并在当前循环中重复使用。如果此循环所需的data.size() 小于前一个循环的data.capacity(),则当前循环永远不需要重新分配。减少访问堆是提高效率的关键。事实上,减少堆的访问正是 move 语义的全部意义所在。

猜测这就是视频中讨论的重点。

我应该强调:如果工厂函数不打算在这样的循环中使用,或者如果工厂函数的返回类型不能使用某些先前的资源(如 capacity() ),那么使用 inOut 参数的重写没有任何好处。

关于c++ - 关于从函数返回大值和 move 的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27369578/

相关文章:

c++ - 发出 double over TCP 套接字的发送/接收 vector (丢失数据)

c++ - move 语义和运算符重载

rust - 为什么 Rust 不允许在一种类型上复制和删除特征?

c++ - 与 std::future 关联的存储是如何分配的?

c++ - 标准库中是否有与 Rust 的 `std::mem::drop` 等效的 C++?

c++ - C++11 类型推导中的右值引用

c++ - 如何用其他文件编译fltk文件?

c++ - 如何将功能齐全的 Web 浏览器嵌入到我的 C++ 应用程序中

c++ - 动态对象的统一初始化

c++ - 具有基于参数类列表的使用 constexpr 的字段的 Variadic CRTP 模板类