簡體   English   中英

帶有 std::unique_ptr 的 BST:在參數中使用右值引用有什么區別?

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

為了練習,我正在使用二叉搜索樹和帶有智能指針的紅黑樹。

節點如下:

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} {}
};

在這里, succ表示有序繼任者,因為我正在解決 CLRS 12.3-5,它說要在沒有與父級鏈接的情況下進行鍛煉。

無論如何,這段代碼運行愉快:

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));
        }
    }

相反,當我在子例程“移植”中刪除右值引用時,它給出了段錯誤:

   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);
        }
    }

崩潰發生在Delete的第 18 行, Transplant(z, std::move(z->right)); .

這里有什么區別? 我被告知std::unique_ptr通常是按值傳遞的。

此外,沒有右值引用的Transplant代碼已在節點與其父節點有鏈接的 BST 代碼中工作。 我不明白為什么。

完整代碼: https://wandbox.org/permlink/zB67AyI43kY6EVqV

當您按值傳遞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

當您通過右值引用傳遞它時,原始指針在 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本身不會移動任何東西,它只是使某些東西成為 xvalue 的強制轉換。 當您從該 xvalue 構造std::unique_ptr<int>參數時,實際移動發生 - 一旦您構造了一個新的std::unique_ptr<int> ,您就失去了原來的那個。 當您通過&&時,您不構造任何東西,您只需傳遞一個引用。

考慮這個調用:

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

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

Transplant中, z->right是一個不擁有任何東西的unique_ptrz->right.get() == nullptr ,因為您剛剛從它移到了第二個Transplant參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM