简体   繁体   English

C ++ 11重读和搬迁:旧版代码可以避免复制吗?

[英]C++11 rvalue and move: Can legacy code avoid copy?

Please understand that I'm still learning the concepts of C++11's move and rvalue semantics. 请理解,我仍在学习C ++ 11的move和rvalue语义的概念。 My question is whether legacy code can get a free lunch of avoiding unnecessary copy by simply using C++11 compilers and STL. 我的问题是,仅使用C ++ 11编译器和STL,遗留代码能否避免不必要的复制而获得免费午餐。

Here is a very simple example. 这是一个非常简单的例子。 This code builds a simple character frequency table for a given string. 此代码为给定的字符串构建一个简单的字符频率表。 For example, "apple" should return {('a', 1), ('e', 1), ('l', 1), ('p', 2)} . 例如,“ apple”应返回{('a', 1), ('e', 1), ('l', 1), ('p', 2)} As you will see, I'm just using vectors as values. 如您所见,我只是将向量用作值。

typedef std::tuple<char, int> Frequency;
typedef std::vector<Frequency> Frequencies;

Frequencies buildFrequenciesTable(std::string w) {
  char table['z' - 'a' + 1] = { 0, };
  std::for_each(w.cbegin(), w.cend(), [&table](char c) {
    ++table[::tolower(c) - 'a'];
  });

  Frequencies freqs;
  for (size_t i = 0; i < 'z' - 'a' + 1; ++i) {
    if (table[i] != 0)
      freqs.push_back(tuple<char, int>((char) ('a' + i), table[i]));
  }
  return freqs; // Q1: Is vector get copied?
}

int main() {
  using namespace std;

  Frequencies f1 = buildFrequenciesTable("apple"); // Q2: Copy?
  Frequencies f2 = buildFrequenciesTable("banana");
  vector<Frequencies> fs = { f1, f2 }; // Q3: Copy?
}

It is clear that C++03 generates all copy code (using copy constructors and assignment operators) when returning vectors as value. 显然,当向量返回值时,C ++ 03会生成所有复制代码(使用复制构造函数和赋值运算符)。 What about in C++11? 在C ++ 11中呢? std::vector a has move constructor. std::vector有移动构造函数。 Can this code get avoid any unnessarry copies? 这段代码可以避免不必要的复制吗? Or, should I use either && or std::forward in the above code? 或者,我应该在以上代码中使用&&还是std::forward

I tried to debug the internal STL code, but it was hard to convince. 我尝试调试内部STL代码,但是很难令人信服。

Note: My goal is to minimize any unnecessary copies in these functions. 注意:我的目标是尽量减少这些功能中的任何不必要的副本。 I know I could use new/pointers/references, but this will need to resolve memory leak problem. 我知道我可以使用new / pointer / references,但这将需要解决内存泄漏问题。 So, I'd like to use values as much as possible. 因此,我想尽可能多地使用值。

For Q1, there is most likely no copy even in C++03 as the copy is removed by the "Named Return Value Optimization" (NRVO). 对于Q1,由于“命名返回值优化”(NRVO)删除了副本,因此即使在C ++ 03中也很可能没有副本。

For Q2, there is also most likely no copy even in C++03 as copy-elision removes it. 对于Q2,即使在C ++ 03中也很可能没有副本,因为复制删除将其删除。

For Q3, even in C++11 you do have copies as you would need to mark f1 and f2 as movable in order to actually have them moved: 对于Q3,即使在C ++ 11中,也确实具有副本,因为您需要将f1f2标记为可移动才能实际移动它们:

vector<Frequencies> fs = { std::move(f1), std::move(f2) };

Since you asked multiple question I think I will omit further explanations, look up NRVO, copy-elision and where std::move is required, ask if you have any further questions. 由于您提出了多个问题,我想我将省略进一步的说明,查找NRVO,复制删除以及在需要std::move位置,请问是否还有其他问题。

However, there are cases where you would get free moves, for example if there is a temporary which could be moved: 但是,在某些情况下您会获得自由举动,例如,如果有一个临时举动可以移动:

vector<Frequencies> fs = { buildFrequenciesTable("apple"),
                           buildFrequenciesTable("bananas") };

The above would detect the two vectors returned from buildFrequenciesTable() as temporaries and therefore they will be moved into fs . 上面将检测从buildFrequenciesTable()返回的两个向量为临时向量,因此它们将被移入fs

Returning the vector from the function ( Q1 ) will use move semantics where possible, without needing to modify the code. 从函数( Q1 )返回向量将在可能的情况下使用移动语义,而无需修改代码。 Likewise initialising the vectors from the returned temporary ( Q2 ) will use move semantics; 同样,从返回的临时变量( Q2 )初始化向量将使用move语义; the return value is an rvalue , so can be moved from. 返回值是一个右值 ,因此可以从中移出。 In practice, both moves (or, historically, copies) should be elided, so that the function initialises f1 and f2 directly with no moving or copying. 在实践中,两个移动(或从历史上来说,都是复制)都应该被删除,以便函数直接初始化f1f2 ,而不进行移动或复制。

Putting them in the vector ( Q3 ) does require copying: variables are lvalues , which can't be implicitly moved. 将它们放入向量( Q3 )中确实需要复制:变量是lvalues ,不能隐式移动。 So you would have to use std::move , or restructure the code, to avoid these copies. 因此,您必须使用std::move或重组代码,以避免这些副本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM