重载函数以使用右值引用而不是左值引用

Overloading a function to use an rvalue reference instead of an lvalue reference

我正在创建一个双向链表模板,我必须编写 2 个构造函数,我们在其中传入一个链表,然后构造一个具有相同节点数以及这些节点中相同数字的新列表.节点的内存位置将是两者之间的唯一区别。

我有两个构造函数。一个是接受 List& 的复制构造函数和一个接受 List&&:

的移动构造函数
List(const List &rhs);
List(List &&rhs);

现在我的问题是,两者的代码是相同的,因为它不是浅拷贝,对吧?区别仅用于编译目的?我的理解是我不需要 std::move 来实现第二个构造函数。

第二个构造函数的目的是能够创建一个使用临时对象作为其参数的列表,例如:L1 = L2+ L3 其中 L2+L3 成为一个临时列表,一旦它被删除已分配给 L1.

如果您只进行浅拷贝,尤其是 non-pointer 值(或任何其他分配的资源),则不需要移动构造函数。

the code is the same for both since it's not a shallow copy, right?

List(const List &rhs)拷贝构造函数需要深拷贝(不是浅拷贝) 从 rhsthisdata(不是节点本身),但在其他方面保持 rhs 完好无损。

List(List &&rhs)移动构造函数预计移动(窃取)节点本身从rhsthis 并让 rhs 处于空状态。

所以,不,他们不会使用相同的代码,甚至不会接近。

operator= 复制赋值 移动赋值 运算符(通常使用 copy/move 构造函数通过 copy-swap idiom 避免代码重复)。

例如:

template<typename T>
class List
{
private:
    struct node
    {
        T data;
        node *previous;
        node *next;
    };

    node *head = nullptr;
    node *tail = nullptr;
    size_t size = 0;

public:
    // default constructor
    List() = default;

    // copy constructor
    List(const List &rhs)
    {
        node **newNode = &head;
        for(node *curNode = rhs.head; curNode; curNode = curNode->next)
        {
            *newNode = new node{curNode->data, tail, nullptr};
            tail = *newNode;
            ++size;
            newNode = &(tail->next);
        }
    }

    // move constructor
    List(List &&rhs)
        : head(rhs.head), tail(rhs.tail), size(rhs.size)
    {
        rhs.head = rhs.tail = nullptr;
        rhs.size = 0;
    }

    // destructor
    ~List()
    {
        node *curNode = head;
        while (curNode) {
            node *next = curNode->next;
            delete curNode;
            curNode = next;
        }
    }

    // copy assignment
    List& operator=(const List &rhs)
    {
        if (this != &rhs) {
            List tmp(rhs);
            std::swap(head, tmp.head);
        }
        return *this;
    }

    // move assignment
    List& operator=(List &&rhs)
    {
        List tmp(std::move(rhs));
        std::swap(head, tmp.head);
        return *this;
    }

    /*
    Alternatively, you can use just 1 implementation of operator=
    for both copy and move assignments, by taking the input parameter
    *by value* and letting the compiler decide which constructor
    to call to initialize it, based on the type of input being assigned:

    // copy/move assignment
    List& operator=(List rhs)
    {
        List tmp(std::move(rhs));
        std::swap(head, tmp.head);
        return *this;
    }
    */
};

L1 = L2 + L3 这样的操作必须实现 operator+,它将 2 个 List 对象作为输入,returns 一个新的 List 对象 data(不是节点)是从 L2L3copied,例如:

template<typename T>
class List
{
    ...
public:
    ...

    List& operator+=(const List &rhs)
    {
        if (rhs.head) {
            List tmp(rhs);
            node **ptr = (tail) ? &(tail->next) : &head;
            *ptr = tmp.head; tmp.head = nullptr;
            tail = tmp.tail; tmp.tail = nullptr;
            size += tmp.size;
        }
        return *this;
    }

    // operator+ can be implemented either as a class member
    // using *this as the left-hand operand...
    List operator+(const List &rhs) const
    {
        List res(*this);
        res += rhs;
        return res;
    }
};

// Or, operator+ can be implemented as a non-member function...
List operator+(const List &lhs, const List &rhs)
{
    List res(lhs);
    res += rhs;
    return res;
}

返回的 List 将是一个临时对象,也称为右值,然后使用 operator=(List&&) 移动赋值 [=61] 将其分配给 L1 =] 运算符(或者,使用 List(List&&) 移动构造函数 operator=(List) 赋值运算符)。