首页 > 解决方案 > 返回 unique_ptr 变量会返回错误

问题描述

我遇到了一个奇怪的问题。以这种方式编写时,我在 setGetDog() 方法中收到错误消息:

class Dog
{
    public:
    void bark() { std::cout << "Bark!" << std::endl; }
};

class test
{
    public:
    std::unique_ptr<Dog> setGetDog()
    {
        if (!dog_)
        {
            dog_ = std::make_unique<Dog>();
        }
        return dog_;
    }

    private:
    std::unique_ptr<Dog> dog_;
};

int main()
{
    auto testClass = std::make_unique<test>();
    auto dog = testClass->setGetDog();
}

错误是这样的:

error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Dog; _Dp = std::default_delete<Dog>]'

但是当我将实现更改为:

    std::unique_ptr<Dog> setGetDog()
    {
        return std::make_unique<Dog>();
    }

它工作得很好。

怎么回事dog_?我不明白为什么错误指出它在test调用该方法时实际上仍然存在时被删除(等等dog_)。

我知道我可以用其他方式写这个,但我特别好奇为什么这个实现上的行为是这样的。

任何人都可以请赐教吗?

标签: c++pointers

解决方案


对象具有几种不同类型的值类别/情况,它们会影响从函数返回的方式。让我们先看看右值类别。当你这样做

std::unique_ptr<Dog> setGetDog()
{
    return std::make_unique<Dog>();
}

std::make_unique<Dog>()按值返回,使返回对象成为右值。这意味着当编译器返回对象时,它将隐式移动它。(编译器也可以省略副本,但这对于本次讨论无关紧要)由于对象已移动,因此您没有任何问题,因为std::unique_ptr它是可移动的。

第二种情况是当你有一个左值,但它是一个函数本地对象。如果你有

std::unique_ptr<Dog> setGetDog()
{
    auto ptr = std::make_unique<Dog>();
    return ptr;
}

ptr是一个左值,但它正在消失,所以我们可以称它为 xvalue(到期值)。对于 xvalues,当您返回它们时,编译器会尝试移动它,因为在这种情况下移动它是一种安全的操作。如果移动操作不存在/不可行,则编译器回退到复制。由于std::unique_ptr是可移动的,因此它可以毫无错误地移动。

最后一种情况是当你有一个像你一样的普通左值时

class test
{
    public:
    std::unique_ptr<Dog> setGetDog()
    {
        if (!dog_)
        {
            dog_ = std::make_unique<Dog>();
        }
        return dog_;
    }

    private:
    std::unique_ptr<Dog> dog_;
};

这里,dog_是类的成员,而不是函数本地的对象。这意味着编译器唯一能做的就是尝试复制。由于您无法复制 a unique_ptr,因此您会收到错误消息。你需要return std::move(dog_);编译它,但如果你dog_在课堂上这样做,那将是空的。


推荐阅读