不变问题和隐式移动构造函数

时间:2012-05-29 10:01:38

标签: c++ move

我读过这篇文章cppnext implicit move,但我不明白这个问题:

#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 个答案:

答案 0 :(得分:1)

本文试图说明的问题是,当在push_back中移动X时,不变量会被破坏。移动后临时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

答案 1 :(得分:1)

  

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

好吧,首先我们尝试来移动push_back调用中的临时X()。我们移动它,或者我们没有[*]。

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

请记住,移动的重点是它从源传输昂贵的复制资源。因此,临时对象不再保证其内部数组包含5个元素,因为移动的目标已经占用了它。

至于为什么它在MSVC 2010中有效 - 记住这篇代码在文章中作为例子在“tweak#1 - 析构函数抑制隐式移动”之上。 C ++ 11确实包含了这个调整 - 如果有一个用户定义的析构函数(12.8 / 9),则不会生成隐式移动构造函数。所以临时是移动,它被复制。

[*]不移动或移动,没有尝试。无论是否移动它都无法实现,标准定义了这一点。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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