移动unique_ptr的语义

时间:2016-03-09 10:26:40

标签: c++ c++14 move-semantics 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.
}

我不明白为什么行u_ptr中的X仍然存在。 毕竟我强迫他被移动(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;
}

4 个答案:

答案 0 :(得分:1)

您没有向我们展示要运行f的代码,但可能是它没有移动指针,即使它有权使用。

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

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

更新:根据您的最新更改,由于移动构造,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;
}

答案 1 :(得分:1)

您对export var upload = function (req: express.Request, res: express.Response, next) { var form = new formidable.IncomingForm(); form.parse(req, function (err, fields, files) { }); form.on('progress', function (bytesReceived, bytesExpected) { // calculating perscent and logging progress message }); form.on('error', function (err) { console.error(err); // --> here I get "Request aborted" }); form.on('end', function (fields, files) { // here I save files to disk with fs-extra and I // send back to client appropriate status }); }的调用未崩溃的原因是它不使用show指针(它不会尝试修改或访问数据成员)。

试试这个:

this
  

void f(std :: unique_ptr&amp;&amp; ref)

这是您最初使用class T{ public: int v; T(){} void show(){ v = 0; std::cout << "HEJ!\n"; } }; 函数获取右值参考&amp;&amp; 时的答案。

您的功能采用右值参考。因此,尚未创建新的f对象,您只需传递unique_ptr

reference功能中,如果使用参数f创建本地unique_ptr,则最终会移动uptr以创建该新对象。

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。就是这样。

答案 2 :(得分:1)

f(std::unique_ptr<T>&& uptr) uptr中的

不是对象 - 它是一个引用。能够捕捉临时变异并使其变异的参考文献。

这就像问为什么在下一个例子中没有克隆对象

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

str_由“常规”引用传递,不会被复制 - 这是通过引用传递的方式。

std::move仅将l值转换为r值引用,uptr中的f(std::unique_ptr<T>&& uptr)可以引用它,它是引用对象的引用。与常见概念相反,std::move不会自行移动,只会将对象转换为移动构造函数/ assg的r值引用。操作员开始。

这里,指针仍然保存有效数据,因为它没有移动,只能转换为r值引用。

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

在你的编辑中,你有不可取的行为,所以一切都可能发生。

答案 3 :(得分:0)

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

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