无法移动而无法从函数返回std :: unique_ptr

时间:2019-04-05 06:39:57

标签: c++ unique-ptr

我正在更新一些旧的代码,这些代码使用auto_ptr来代替unique_ptr。这主要是一项搜索和替换工作,但我发现我在某个代码返回unique_ptr的地方收到编译错误。

以下是说明问题的示例:

#include <string>
#include <iostream>
#include <memory>
#include <utility>

using namespace std;

struct Foo {
    unique_ptr<string> us;

    Foo() {
        this->us = unique_ptr<string>(new string("hello"));
    }

    unique_ptr<string> bar() {
        return this->us;
    }

};


int main(int argc, const char **argv) {

    Foo foo;
    unique_ptr<string> s = foo.bar();
    cout << *s << endl;

}

当我编译它时,我得到了:

t1.cpp: In member function ‘std::unique_ptr<std::basic_string<char> > Foo::bar()’:
t1.cpp:17:16: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::basic_string<char>; _Dp = std::default_delete<std::basic_string<char> >]’
   return this->us;
                ^~
In file included from /opt/rh/devtoolset-7/root/usr/include/c++/7/memory:80:0,
                 from t1.cpp:4:
/opt/rh/devtoolset-7/root/usr/include/c++/7/bits/unique_ptr.h:388:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^~~~~~~~~~

如果我更改错误行以指定这样的举动:

        return move(this->us);

然后它起作用。

我发现有很多参考资料表明不需要搬家-例如this SO question和来自铬项目的these guidelines

我的问题是:为什么在这种情况下需要明确指定移动?我以某种方式返回实例变量的值与事实有关吗?

如果这是一个骗子,请提前道歉-我敢肯定会问过这个问题,但是我很难找到能找到它的搜索词。

5 个答案:

答案 0 :(得分:5)

所有与语义有关。 unique_ptr唯一所有权,因此它将被返回或以us的形式返回,但不能同时返回。该标准的规则幸运地保护您免受这种误解。

您可以做的是返回一个临时unique_ptr(将被移动):

unique_ptr<string> bar() {
    return make_unique<string>("hello, world");
}

但是,如果您需要在多个地方复制unique_ptrmain()似乎就是这种情况,那么您并不是在寻找唯一的所有权,而是共享所有权< / strong>。因此,您应该转到shared_ptr

struct Foo {
    shared_ptr<string> us;

    Foo() {
        us = make_shared<string>("hello, world");
    }
    shared_ptr<string> bar() {
        return us;
    }
};

int main(int argc, const char **argv) {
    Foo foo;
    auto s = foo.bar();
    cout << *s << endl;
}

答案 1 :(得分:4)

您找到的所有引用都提到返回的对象必须是函数局部变量。您返回一个成员。返回时,成员变量不会被隐式视为右值。

并非没有道理。除非明确地完成,否则一个类通常不希望失去其资源的所有权。

当函数返回时,局部函数将要死亡,因此让编译器隐式地蚕食它是一个值得优化的优化方法。

答案 2 :(得分:3)

您正尝试返回Foo::us类型的std::unique_ptr<>成员变量。 std::unique_ptr<>的唯一目的是不可复制但可移动。这就是为什么返回需要std::move才能将Foo::us成员变量重置为null的原因。

bar函数可能应该通过值或const引用返回该std::string

string const& bar() const {
    return *this->us;
}

然后:

int main() {
    Foo foo;
    string const& s = foo.bar();
    cout << s << endl;
}

答案 3 :(得分:2)

std::auto_ptr的正确使用与std::unique_ptr的用途相同,它拥有对单个对象的唯一引用,该引用在指针被破坏时被删除。

std::auto_ptr早于移动语义,因此它具有一个复制构造函数,其行为类似于移动构造函数。意外地复制std::auto_ptr却不知道原来的std::auto_ptr现在是空的,很容易。

由于std::unqiue_ptr仅可移动而不能复制,因此可以避免这种情况。您必须使用std::move明确放弃对指针的所有权。

例外情况是为本地堆栈分配了std::unique_ptr,无论如何它们都将在当前作用域的末尾销毁,因此该语言允许您不使用{{1}而返回std::unique_ptr }。由于您的std::move变量不是局部变量,因此不允许直接将其返回,以防止意外释放所有权。

答案 4 :(得分:1)

这显示了auto_ptr的确切危险以及为何将其删除。

如果您想两次调用该函数,则可以看到问题所在。

auto_ptr<string> Foo::bar() {
    return this->us;
}

int func() {
  Foo foo;
  auto_ptr<string> a = foo.bar();
  auto_ptr<string> b = foo.bar();//uh oh, null ptr
}

如果您喜欢这种行为,则可以使用unique_ptr轻松模拟它。

#include <string>
#include <iostream>
#include <memory>
#include <utility>

using namespace std;

struct Foo {
    unique_ptr<string> us;

    Foo() {
        this->us = unique_ptr<string>(new string("hello"));
    }

    unique_ptr<string> bar() {
        unique_ptr<string> local_us(us.release());
        return local_us;
    }

};


int main(int argc, const char **argv) {

    Foo foo;
    unique_ptr<string> a = foo.bar();
    unique_ptr<string> b = foo.bar();//b is a nullptr
    cout << *s << endl;

}