【问题标题】:C++11 Move semantics behaviour specific questionsC++11 移动语义行为特定问题
【发布时间】:2013-01-15 04:39:41
【问题描述】:

我已经阅读了下面的文章,它对移动语义有了很好的了解:

Can someone please explain move semantics to me?

但我仍然无法理解以下有关移动语义的内容 -

  1. 复制省略和 RVO 是否仍然适用于没有移动构造函数的类?

  2. 即使我们的类没有移动构造函数,但 STL 容器却有。对于像

  3. 这样的操作

std::vector<MyClass> vt = CreateMyClassVector();

并执行排序等操作。为什么 STL 不能在内部利用移动语义在内部使用不需要移动构造函数的复制省略或 RVO 等操作来改进此类操作?

3。 在以下情况下,我们是否会从移动语义中受益 -

std::vector< int > vt1(1000000, 5); // Create and initialize 1 million entries with value 5

std::vector< int > vt2(std::move(vt1)); // move vt1 to vt2

由于整数是原始类型,移动整数元素不会提供任何优势。 或者在移动操作之后 vt2 只是指向堆中的 vt1 内存,并且 vt1 设置为空。实际发生了什么?如果是后者,那么即使第 2 点也认为我们的类可能不需要移动构造函数。

4。 当使用 std::move 左值调用 push_back() 时,例如:

    std::vector<MyClass> vt;

    for(int i=0; i<10; ++i)
    {
        vt.push_back(MyClass());
    }

    MyClass obj;

    vt.push_back(std::move(obj));

现在,由于向量具有连续的内存分配,并且 obj 在内存中的其他位置定义,移动语义如何将 obj 内存移动到向量 vt 连续内存区域,在这种情况下不移动内存与复制内存一样好,如何move 是否通过简单地移动指向堆不同区域中内存的指针来证明向量连续内存需求的合理性?

提前感谢您的解释!

[最初发布为Move semantics clarification,但现在由于上下文发生了一些变化,将其发布为新问题应尽快删除旧问题。]

【问题讨论】:

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


    【解决方案1】:

    复制省略和 RVO 是否仍然适用于没有移动构造函数的类?

    是的,RVO 仍然起作用。实际上,编译器应该选择:

    • RVO(如果可能)
    • 移动构造(如果可能)
    • 复制构造(最后的手段)

    为什么 STL 不能在内部利用移动语义来在内部使用不需要移动构造函数的复制省略或 RVO 等操作来改进此类操作?

    STL 容器可移动的,与存储在其中的类型无关。但是,对容器in中的对象的操作需要对象的配合,因此sort(例如)只能在对象可移动的情况下移动对象。

    在以下情况 [...] 因为整数是原始类型,我们是否会从移动语义中受益?

    是的,您可以这样做,因为容器可移动的,无论其内容如何。正如您所推断的,st2 将从st1 窃取内存。 st1 移动后的状态是不确定的,所以我不能保证它的存储会被取消。

    push_back() 在左值上使用std::move 调用时[会发生什么]?

    调用左值类型的移动构造函数,通常这涉及将原件按位复制到目的地,然后将原件无效。

    一般来说,移动构造函数的成本与sizeof(object)成正比;例如,无论std::string 有多少个字符,sizeof(std::string) 都是稳定的,因为实际上这些字符存储在堆上(至少当它们有足够数量时),因此只有 指针 到堆存储被移动(加上一些元数据)。

    【讨论】:

    • 谢谢,你提到容器是可移动向量 st2 会从 st1 窃取内存,你的意思是 st2 现在将指向 st1 对象内存布局(就像 RVO)并且不需要移动每个整数成员st1。然后在字符串对象的后一种情况下,它为什么只需要复制大小和指向缓冲区的指针,而是它可以直接指向字符串对象的对象内存布局并将原始对象设置为 null,有点像 RVO 的工作方式,它只是创建一个对临时的新引用对象。
    • @Goku: 1/ Vector:是的,指向日期/大小/容量的指针以某种方式从st1转移st2。 2/ 字符串:同样的情况,指向日期/大小/容量的指针从一个字符串转移到另一个。
    【解决方案2】:
    1. 是的。
    2. 他们尽可能这样做。
    3. 是的。 std::vector 有一个移动构造函数,可以避免复制所有元素。
    4. 它仍然是连续的。

    例如

    struct MyClass
    {         
         MyClass(MyClass&& other) 
             : xs(other.xs), size(other.size)
         {
              other.xs = nullptr;
         }
    
         MyClass(const MyClass& other) 
           : xs(new int[other.size]), size(other.size)
         { 
             memcpy(xs, other.xs, size);
         }
    
         ~MyClass() 
         {               
             delete[] xs;
         }
    
         int* xs;
         int size;
    }
    

    使用移动构造函数,只需将xssize 复制到向量中(用于连续内存),但是我们不需要像在复制构造函数中那样执行内存分配和memcpy

    【讨论】:

    • 谢谢@ronag:标准是否说明了第 2 点。我还没有真正理解您对第 3 点的评论,是的,向量确实有移动构造函数,但它在成员明智的移动(这是等效的为所有元素复制整数情况)或简单地使向量 vt2 指向向量 v1 的堆分配。对于没有为向量元素分配内存的第 4 点,您将如何将 xs 复制到它,如果您只是复制指针,那么它将指向不在向量连续内存中的内存区域
    • 2. STl 以与任何其他代码相同的方式使用 RVO 和类似的优化。 3. 它不进行成员移动,它的工作方式与我的 MyClass 示例类似。 4. 只有 MyClass(即它的成员 xs 和 size)需要在内存中连续,MyClass 是否有指向其他地方的指针无关紧要。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-16
    • 2019-01-02
    • 2014-03-18
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    相关资源
    最近更新 更多