为什么std :: make_move_iterator适用于vector <string>,但不适用于vector <int>

Min*_*ine 8 c++ string vector move-semantics c++11

我原以为这std::make_move_iterator总会移动内容,但似乎没有.

看起来它正在移动元素vector<string>而不是移动元素vector<int>.

请参阅以下代码段:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

void moveIntVector()
{
  std::cout << __func__ << std::endl;
  std::vector<int> v1;
  for (unsigned i = 0; i < 10; ++i) {
    v1.push_back(i);
  }
  std::vector<int> v2(
    std::make_move_iterator(v1.begin() + 5),
    std::make_move_iterator(v1.end()));
  std::cout << "v1 is: ";
  for (auto i : v1) {
    std::cout << i << " ";
  }
  std::cout << std::endl;

  std::cout << "v2 is: ";
  for (auto i : v2) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
}

void moveStringVector()
{
  std::cout << __func__ << std::endl;
  std::vector<std::string> v1;
  for (unsigned i = 0; i < 10; ++i) {
    v1.push_back(std::to_string(i));
  }
  std::vector<std::string> v2(
    std::make_move_iterator(v1.begin() + 5),
    std::make_move_iterator(v1.end()));
  std::cout << "v1 is: ";
  for (auto i : v1) {
    std::cout << i << " ";
  }
  std::cout << std::endl;

  std::cout << "v2 is: ";
  for (auto i : v2) {
    std::cout << i << " ";
  }
  std::cout << std::endl;
}

int main()
{
  moveIntVector();
  moveStringVector();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

moveIntVector
v1 is: 0 1 2 3 4 5 6 7 8 9  # I expect this should be `0 1 2 3 4` as well!
v2 is: 5 6 7 8 9 
moveStringVector
v1 is: 0 1 2 3 4      
v2 is: 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

我正在Ubuntu 14.04, gcc 4.8.2编写代码-std=c++11

你能解释为什么std::make_move_iterator有不同的行为vector<int>vector<string>?(或者它是一个错误吗?)

jua*_*nza 20

行为是预期的.两个向量的移动使得原始v1元素在下半部分中有5个移动元素.

不同之处在于,当移动字符串时,留下的是空字符串.这是因为它是一种非常有效的方式来移动字符串,并使移动的字符串处于自洽状态(从技术上讲,它们可以保留值"Hello, World, nice move!",但这会产生额外的成本).最重要的是,您不会在输出中看到那些移动的字符串.

int向量的情况下,没有办法移动int比复制它更有效的方法,所以它们只是复制过来.

如果检查向量的大小,则v1在两种情况下都会看到大小为10.

这是一个简化的例子来说明从字符串移动是空的:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

int main() 
{
    std::vector<std::string> v1{"a", "b", "c", "d", "e"};
    std::vector<std::string> v2(std::make_move_iterator(v1.begin()),
                                std::make_move_iterator(v1.end()));

    std::cout << "v1 size " << v1.size() << '\n';
    std::cout << "v1: ";
    for (const auto& s : v1) std::cout << s << " - ";
    std::cout << '\n';

    std::cout << "v2 size " << v2.size() << '\n';
    std::cout << "v2: ";
    for (const auto& s : v2) std::cout << s << " - ";
    std::cout << '\n';
}
Run Code Online (Sandbox Code Playgroud)

输出:

v1 size 5
v1:  -  -  -  -  - 
v2 size 5
v2: a - b - c - d - e - 
Run Code Online (Sandbox Code Playgroud)

  • +1,但我的方式不同:有一种有效的方法来移动`int`,最有效的方法是复制它.(我将"a"定义为"a"到"b",确保`b`得到`a`的值,而`a`保持有效状态.复制总是一种移动方式,即使对于`string `,恰好这对`string`来说不是一个特别好的方法.) (6认同)

Gal*_*lik 5

当我们谈论移动时,我们并不是在谈论移动对象本身(它保持完整)。移动的是其内部数据。这可能会或可能不会影响内部数据被移动的对象的值。

这就是为什么int数组不丢失其原始ints的原因。对于您的字符串示例,它仍然std::stringsint示例一样具有原始字符串,但是其内部值已更改为空字符串。

重要的是要记住,在内部std::string(本质上)持有指向字符数组的指针。因此,当您复制 a 时,您std::string复制字符数组的每个元素。一,然而,避免做所有通过复制内部指针,而不是复制。

但是,如果移动操作在此停止,则将使std::strings指向相同的字符数组,并且更改其中一个指向的字符数据std::string也会更改另一个。因此,当移动字符串时,仅复制内部指针是不够的,您必须将指针的内部指针std::string从点移动到新的空白字符数组,以使其不再影响将其数据移至的字符串。 。

移动int时,在复制数据之后无需采取进一步的措施。由于不涉及任何指针,因此在复制之后,两个int都包含独立的数据。