c++ 当你在双端队列中 push_back 或 push_front 时真正发生了什么

c++ what really happens when you push_back or push_front in a deque

我正在阅读这里: http://www.cplusplus.com/reference/deque/deque/push_back/

void push_back (const value_type& val);

val 是一个

Value to be copied (or moved) to the new element.

例如如果 val 是 class 的实例,在哪种情况下它会被复制并移动?这两种情况有什么区别?

您能否也解释一下以下 add_val1、add_val2 和 add_val3 中的每一个有何不同?

#include <deque>

class Value
{
    int member1;
    int member2;
};


class ValuesContainer
{
private:
    std::deque<Value> m_vals;

public:

    void add_val1(const Value &val)
    {
        m_vals.push_back( val );
    }

    void add_val2(const Value val)
    {
        m_vals.push_back( val );
    }

    void add_val3(const Value &val)
    {
        m_vals.push_back( Value(val) );
    }

};

int main()
{
    return 0;
}

谢谢!

Value to be copied (or moved) to the new element.

这取决于 push_back 中使用的重载(参见 this,在我看来,更好的参考页面)。

add_val1 参考,push_back 复制。
add_val2 获取值(因此函数将复制它)然后由 push_back.
再次复制 add_val3 获取引用,您调用一个副本,但由于 Value(val) 是 r 值,因此临时文件将移动 push_back

if val is an instance of a class, in which case does it get copied and in which it gets moved? What is the difference between the two cases?

如果一个值是 class 的一个实例,比方说

class MyValue {};

MyValue originalValue;
MyValue copyAssignedValue = originalValue;

它总是被复制。发生这种情况是因为调用了复制赋值运算符。在类似的事情中,

MyValue copyConstructedValue(originalValue);

调用复制构造函数(顾名思义,它复制内容)。

要了解为什么以及何时使用复制或移动构造函数,有必要了解这两种操作之间的区别:

复制对象就像普通的复制和粘贴:您现在拥有同一对象的两个副本,您可以独立处理它们。移动一个对象就像剪切和粘贴:最后只有一个工作对象,旧的被删除。但是,它比复制快得多,因为您不需要分配新内存。

要移动对象,您可以做几件事。例如,您可以创建一个编译器知道您只需要在移动构造函数中使用的对象。

MyValue moveConstructedValue(MyValue());

为了清楚起见,这就是移动构造函数的样子:

MyValue::MyValue(MyValue&& other);

注意双 &&,表示右值引用(单个 & 现在称为左值引用)。

或者您可以表明要显式使用移动构造函数(注意,originValue 对象将被重置为默认值):

MyValue valueToBeMovedTo(std::move(originalValue));

回答你的第二个问题,你的哪个函数使用了移动操作:实际上,none 其中 "move" 将 val 转换为 m_vals,因为编译器不知道如果你想以后使用它。您可以通过使用新的右值引用来移动:

void add_valMoving(const Value &&val)
{
   m_vals.push_back( val );
}

为了100%正确,add_val3使用移动构造函数,但不是直接在val对象上:它首先调用Value的复制构造函数并将val复制到一个新对象中,然后使用移动构造函数将新对象移动到双端队列中。


编辑:

为了在移动对象时更清楚"default values",移动构造函数可以是:

class MyValue 
{
public:
    MyValue(MyValue&& other)
    {
       this->member = other.member;
       other.member = 0;
    }

private:
    int member;
}

但是,您永远不知道移动后原始对象将设置为什么值。

那个引用让你感到困惑,因为它默认显示 复制 push_back,并且还在谈论移动。

自C++11起,有两个 push_backs

  • void push_back( const T& value ); 总是复制
  • void push_back( T&& value ); 总是移动

如果你推回的值有一个名称,或者明确是一个T&,那么它将使用第一个,否则使用第二个

void add_val1(const Value &val)
{
    m_vals.push_back( val ); // copy
}

void add_val2(const Value val)
{
    m_vals.push_back( val ); // copy
}

void add_val3(const Value &val)
{
    m_vals.push_back( Value(val) ); // move the temporary that is a copy of val
}

extern Value make_value_somehow() 

void add_val4()
{
    m_vals.push_back( make_value_somehow() ); // move
}

extern Value& find_value_somehow() 

void add_val5()
{
    m_vals.push_back( find_value_somehow() ); // copy
}

void add_val6(Value val)
{
    m_vals.push_back( val ); // copy
}

void add_val7(Value val)
{
    m_vals.push_back( std::move(val) ); // move
}

void add_val8(const Value val)
{
    // m_vals.push_back( std::move(val) ); // ill-formed, can't modify val
}

void add_val9(Value & val)
{
    m_vals.push_back( std::move(val) ); // move, which will confuse people
}