0

我读了这篇文章cppnext 隐式移动,但我不明白这个问题:

#include <iostream>
#include <vector>
struct X
{
    // invariant: v.size() == 5
    X() : v(5) {}

    ~X()
    {
        std::cout << v[0] << std::endl;
    }

 private:    
    std::vector<int> v;
};

int main()
{
    std::vector<X> y;
    y.push_back(X()); // X() rvalue: copied in C++03, moved in C++0x
}

在 MSVC2010 下运行时没有错误...有人可以帮我吗?

在这篇文章中有这句话:

这里的关键问题是,在 C++03 中,X 有一个不变量,它的 v 成员总是有 5 个元素。X::~X() 依靠该不变量,但新引入的移动构造函数从 v 移动,从而将其长度设置为零。

我不明白为什么,因为我们试图移动 X,v长度会为零

4

4 回答 4

1

本文试图说明的问题是,当 anX在 push_back 中移动时,不变量被破坏。移动后临时X的向量v将为空,因此析构函数调用将调用未定义的行为。可能是因为您的编译器没有实现移动语义,或者因为未定义的行为不会通过纯粹的机会导致任何可察觉的运行时错误,所以您没有看到任何问题。

您可以使用这个简单的程序检查此行为,我们在其中显式移动一个X实例:

#include <vector>
#include <iostream>
struct X {
  X() : v(5) {}
  std::vector<int> v;
};

int main() {
  X x0;
  std::cout << x0.v.size() << ", ";
  X x1 = std::move(x0);
  std::cout << x0.v.size() << "\n";
}

在 GCC 4.7 上,这些产生

5, 0

这来自std::vector' 的移动构造函数,由X' 隐式生成的构造函数使用。您可以std::vector直接检查:

int main() {
  std::vector<int> v0(5);
  std::cout << v0.size() << ", ";
  std::vector<int> v1 = std::move(v0);
  std::cout << v0.size() << "\n";
}

这会产生相同的输出。

现在,在您引用的示例中,用户定义的析构函数的存在意味着没有隐式生成的移动构造函数,X因此不会push_back.

于 2012-05-29T10:09:18.147 回答
1

我不明白为什么,因为我们试图移动 X,v 长度将为零

好吧,首先我们不要尝试X()在 push_back 调用中移动临时。我们确实移动它,或者我们不移动它[*]。

如果我们要移动它(使用隐式生成的移动构造函数),它的数据成员v将被移动。这篇文章的假设是,从向量中移动会使它为空,尽管我不记得这是必需的还是典型的。但它确实会v处于无法保证您可以访问其以前的元素的状态。

请记住,移动的全部意义在于它从源转移复制成本高昂的资源。所以临时对象不再保证其内部数组有 5 个元素,因为移动的目的地已经取走了它。

至于为什么它在 MSVC 2010 中起作用——请记住,此代码在文章中作为示例显示在“调整 #1——析构函数抑制隐式移动”上方。实际上,C++11 确实包含此调整——如果存在用户定义的析构函数 (12.8/9),则不会生成隐式移动构造函数。所以临时没有移动,它被复制了。

[*] 动或不动,没有尝试。是否移动它不取决于实现,标准对此进行了定义。

于 2012-05-29T10:17:52.470 回答
1

MSVC2010 没有实现 auto=generated move 构造函数,所以即使你的代码会生成一个,你也不会在那里看到它。因此,与往常一样,“随机实现 X 做了 Y”是一个毫无意义的陈述。

于 2012-05-29T10:45:20.787 回答
0

在 VS2010 中,临时文件会被复制然后销毁。使用移动语义,临时对象将仅用于向量。

于 2012-05-29T10:07:10.630 回答