简体   繁体   English

带有 std::unique_ptr 的 BST:在参数中使用右值引用有什么区别?

[英]BST with std::unique_ptr: What's difference in using rvalue reference in parameters?

For practice, I'm playing with a binary search tree and a red-black tree with smart pointers.为了练习,我正在使用二叉搜索树和带有智能指针的红黑树。

The Node is as follows:节点如下:

template <typename T>
struct BSTNode {
    T key;
    std::unique_ptr<BSTNode<T>> left;
    std::unique_ptr<BSTNode<T>> right;
    BSTNode<T>* succ;

    BSTNode(const T& key) : key {key}, succ {nullptr} {}
};

Here, succ means inorder successor, because I'm solving CLRS 12.3-5 which said to do exercise without having a link to parent.在这里, succ表示有序继任者,因为我正在解决 CLRS 12.3-5,它说要在没有与父级链接的情况下进行锻炼。

Anyway, this code works happily:无论如何,这段代码运行愉快:

void Transplant(BSTNode<T>* u, std::unique_ptr<BSTNode<T>>&& v) {
        auto p = Parent(u);
        if (!p) {
            root = std::move(v);
        } else if (u == p->left.get()) {
            p->left = std::move(v);
        } else {
            p->right = std::move(v);
        }
    }

    void Delete(BSTNode<T>* z) {
        if (!z) {
            return;
        }
        BSTNode<T>* pred = nullptr;
        if (z->left) {
            pred = Maximum(z->left.get());
        } else {
            auto y = Parent(z);
            auto x = z;
            while (y && x == y->left.get()) {
                x = y;
                y = Parent(y);
            }
            pred = y;
        }
        pred->succ = z->succ;
        if (!z->left) {
            Transplant(z, std::move(z->right));
        } else if (!z->right) {
            Transplant(z, std::move(z->left));
        } else {
            auto y1 = z->right.get();
            std::unique_ptr<BSTNode<T>> y;
            if (!y1->left) {
                y = std::move(z->right);
            } else {
                while (y1->left) {
                    y1 = y1->left.get();
                }
                y = std::move(Parent(y1)->left);
            }
            if (Parent(y.get()) != z) {
                Transplant(y.get(), std::move(y->right));
                y->right = std::move(z->right);
            }
            y->left = std::move(z->left);
            Transplant(z, std::move(y));
        }
    }

In contrast, when I remove the rvalue reference in the subroutine "Transplant", it gives segfault:相反,当我在子例程“移植”中删除右值引用时,它给出了段错误:

   void Transplant(BSTNode<T>* u, std::unique_ptr<BSTNode<T>> v) { // crashes
        auto p = Parent(u);
        if (!p) {
            root = std::move(v);
        } else if (u == p->left.get()) {
            p->left = std::move(v);
        } else {
            p->right = std::move(v);
        }
    }

the crash occurs at the 18th line of Delete , Transplant(z, std::move(z->right));崩溃发生在Delete的第 18 行, Transplant(z, std::move(z->right)); . .

What is the difference here?这里有什么区别? I've been told that std::unique_ptr s are usually passed by values.我被告知std::unique_ptr通常是按值传递的。

Moreover, the code of Transplant without rvalue reference has been worked in the BST code that a node has a link to its parent.此外,没有右值引用的Transplant代码已在节点与其父节点有链接的 BST 代码中工作。 I don't see why.我不明白为什么。

Full code: https://wandbox.org/permlink/zB67AyI43kY6EVqV完整代码: https://wandbox.org/permlink/zB67AyI43kY6EVqV

When you pass a unique_ptr by value, you lose the original pointer:当您按值传递unique_ptr时,您会丢失原始指针:

void use_ptr(std::unique_ptr<int>) {}

std::unique_ptr<int> ptr = std::make_unique<int>(999);
use_ptr(std::move(ptr));
// here ptr no longer contains a pointer to 999
assert(!ptr);
std::cout << *ptr; // this is UB

When you pass it by rvalue reference, the original pointer remains non-null after function invocation:当您通过右值引用传递它时,原始指针在 function 调用后保持非空:

void use_ptr(std::unique_ptr<int>&&) {}

std::unique_ptr<int> ptr = std::make_unique<int>(999);
use_ptr(std::move(ptr));
// ptr still manages a pointer to `999`
assert(ptr);
std::cout << *ptr; // this is not UB

std::move itself doesn't move anything, it's just a cast to make something an xvalue. std::move本身不会移动任何东西,它只是使某些东西成为 xvalue 的强制转换。 The actual move occurs when you construct std::unique_ptr<int> argument from that xvalue - once you've constructed a new std::unique_ptr<int> , you've lost the original one.当您从该 xvalue 构造std::unique_ptr<int>参数时,实际移动发生 - 一旦您构造了一个新的std::unique_ptr<int> ,您就失去了原来的那个。 When you pass by && you don't construct anything, you simply pass a reference.当您通过&&时,您不构造任何东西,您只需传递一个引用。

Consider this call:考虑这个调用:

Transplant(z, std::move(z->right));

With

void Transplant(BSTNode<T>*, std::unique_ptr<BSTNode<T>>)

inside Transplant , z->right is a unique_ptr that doesn't own anything, z->right.get() == nullptr , because you've just moved from it into the second Transplant argument.Transplant中, z->right是一个不拥有任何东西的unique_ptrz->right.get() == nullptr ,因为您刚刚从它移到了第二个Transplant参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM