繁体   English   中英

unique_ptr的移动语义

[英]Moving semantics of unique_ptr

让我们考虑以下代码:

template<typename T>
void f(std::unique_ptr<T>&& uptr) { /*...*/ }

在另一个功能中:

void g()
{
    std::unique_ptr<ANY_TYPE> u_ptr = std::make_unique<ANY_TYPE>();
    f(std::move(u_ptr));
X:  u_ptr->do_sth(); // it works, I don't understand why. Details below.
}

我不明白为什么X行中的u_ptr仍然存在。 毕竟,我迫使他感动(std :: move)。

---EDIT---
Ok, so now:
The code is still working:

class T{
    public:
    T(){}

    void show(){
        std::cout << "HEJ!\n";
    }
};

void f(std::unique_ptr<T> ref){
   ref->show();   
}

int main()
{
    std::unique_ptr<T> my;
    my->show();
    f(std::move(my));
    my->show(); // How is it possible. Now, f takes unique_ptr by value 


    return 0;
}

您没有向我们展示该函数f代码,但是即使它具有许可,也可能没有移动指针。

您通过引用传递了unique_ptr 如果函数调用确实移动了它,那么该函数将无法使用它,因为它将在函数有机会消失之前就消失了。

如果希望函数调用实际移动指针,则需要按值传递指针,而不是引用。 该值将是一个要移入的unique_ptr 在这种情况下,应声明该函数采用std::unique_ptr<T>而不是std::unique_ptr<T>&& 然后,您实际上可以在调用函数时调用move构造函数。

更新 :由于您的最新更改,由于移动构造, unique_ptr将不再引用任何有效对象。 您只是从未检查过它。 无论对象是有效的还是销毁的,调用不访问任何成员变量的非虚拟方法都可以相同地工作,因为该对象不需要任何东西。 您也从未使unique_ptr实际上指向任何东西。

而是使unique_ptr指向某物。 移动后,尝试调用虚拟函数或访问其值被析构函数更改的成员。 像这样:

#include <iostream>
#include <memory>

class T{
    public:
    T() : valid (true) {}
    ~T() { valid = false; }

    bool valid;

    void show(){
        std::cout << "HEJ! " << valid << std::endl;
    }
};

void f(std::unique_ptr<T> ref){
   ref->show();   
}

int main()
{
    std::unique_ptr<T> my (new T); // Make it point to a new object
    my->show();
    f(std::move(my));
    my->show(); // Try to access


    return 0;
}

您的show调用不会崩溃的原因是因为它不使用this指针(它不会尝试修改或访问数据成员)。

尝试这个:

class T{
    public:
    int v;
    T(){}

    void show(){
        v = 0;
        std::cout << "HEJ!\n";
    }
};

无效f(std :: unique_ptr && ref)

这是您最初让f函数采用右值引用&&时的答案。

您的函数带有右值引用 因此,还没有创建新的unique_ptr对象,您只是传递了reference

f函数内部,如果使用参数uptr创建一个本地unique_ptr ,则最终将移动uptr来创建该新对象。

template<typename T>
void f(std::unique_ptr<T>&& uptr)
{
     //move uptr into local_unique_ptr 
     //note that we have to use move again
     //because uptr has a name, therefore its a lvalue.
     auto local_unique_ptr = std::unique_ptr<T>(std::move(uptr));
}

要始终知道的重要事情是std::move仅仅是static_cast

如果将lvalue传递给std::move ,它将返回一个rvalue 如果传递一个rvalue ,它将返回一个rvalue 而已。

f(std::unique_ptr<T>&& uptr) uptr不是对象-它是引用。 能够捕获临时变量并对其进行突变的参考。

就像问下一个示例为什么不克隆对象

void func(std::string& str);
std::string str_ = "yyy";
func(str_);

str_由“常规”引用传递,不会被复制-这就是引用传递的意思。

std::move仅投-1-值r值参考,其uptrf(std::unique_ptr<T>&& uptr)可以引用,它是引用一个对象的引用。 与通常的概念相反, std::move本身不会做任何移动,只会将对象强制转换为move构造函数/ assg的r值引用。 操作员加入。

在这里,由于指针没有被移动,因此仍然保留有效数据,仅将其转换为r-value-reference。

如果要移动对象,则必须将参数声明为对象,而不是引用: f(std::unique_ptr<T> uptr)

在编辑中,您有不确定的行为,因此一切都会发生。

您的函数f实际上可能不会移动指针。 仅通过&&取得对象不会修改该对象。

u_ptr->do_sth()可以调用静态成员函数或不访问对象的成员函数( this ),这就是为什么它不会崩溃的原因。

暂无
暂无

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

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