初始化unique_ptr会导致“错误:使用已删除的函数”,即使它是“std::move”ed

Initializing unique_ptr causes an "error: use of deleted function" even though it's "std::move"ed

提问人:user2465084 提问时间:6/25/2022 最后编辑:user2465084 更新时间:6/25/2022 访问量:571

问:

我正在编写代码,这些代码通过几个看起来很糟糕的层,但我别无选择,只能暂时将其全部传递。std::unique_ptr

问题是当我尝试将 传递给类的构造函数时,我遇到了错误。在调用点,已使用其他地方的参数初始化/构造。std::unique_ptrProviderChild::function1()ChildImpl&std::unique_ptr<Retriever>

有人可以告诉我为什么我会遇到这个错误,以及如何获得这个工作吗?我想知道是不是因为继承的发生,但我不知道除了搬家得到这份工作之外还能做什么......(除了不要通过所有这些看起来与相同构造函数相似的类来传递此 PTR 之类的东西......哈哈)

// child.cpp
Child::Child(Impl& child_impl, std::unique_ptr<Retriever> child_retriever_up)
: Parent(child_impl, std::move(child_retriever_up))
{}

T::Y Child::function1() const
{
    **Provider provider(d_impl, d_retriever_up);** 
    // these d_impl and d_retriever are in Parent class.
    ...
    return Y;
}

// child.h
class Child : public Parent {
public:
    // creators
    explicit Child(Impl& child_impl, std::unique_ptr<Retriever> child_retriever_up);
    ~Child() override = default;
    // accessors
    T::Y Child::function1() const;
private:
    Child(const Child&) = delete;
    Child& operator=(const Child&) = delete;
// parent.cpp
Parent::Parent(Impl& impl, std::unique_ptr<Retriever> retriever_up)
: d_impl(impl), d_retriever_up(std::move(retriever_up))
{}

// parent.h
class Parent {
public:
    // creators
    explicit Parent(Impl& impl, std::unique_ptr<Retriever> retriever_up);
    virtual ~Parent() = default;
    // copy constructor
    Parent(const Parent& parent) 
    : d_context(parent.d_impl)
    , d_retriever_up(std::move(*parent.d_retriever_up))
    {
    }
    // data
    Impl& d_impl;
    std::unique_ptr<Retriever> d_retriever_up;
private:
    //Parent(const Parent&) = delete;
    Parent& operator=(const Parent&) = delete;
// provider.cpp
Provider::Provider(Impl& impl, std::unique_ptr<Retriever> retriever_up)
: d_impl(impl)
, d_retriever(std::move(retriever_up))
{}

// provider.h
class Provider {
public:
    Provider(Impl& context, std::unique_ptr<Retriever> retriever_up);
    //copy contstructor
    Provider(const Provider& provider) 
    : d_impl(provider.d_impl)
    , d_retriever(std::move(*provider.d_retriever))
    {
    }

private:
    Impl& d_impl;
    std::unique_ptr<Retriever> d_retriever;
}
C++ C++11 Unique-PTR stdmove 删除函数

评论

1赞 Sam Varshavchik 6/25/2022
“这些d_impl和d_retriever都在 Parent 类中”——这似乎不是真的。(部分)显示的 Parent 类没有任何名为 的成员。即使是这样,那也是你的问题。由于这是一个 lvaluate,并且构造函数参数是按值复制的,因此这需要复制构造函数。其中,在删除的情况下。但是,由于显示的代码无法满足最小可重现示例的要求,因此无法权威地声明和解释这一点,作为答案。d_retrieverunique_ptr
0赞 user2465084 6/25/2022
哎呀,我在d_retriever上很糟糕。我现在正在编辑@SamVarshavchik
2赞 Sam Varshavchik 6/25/2022
所以,这不是真正的代码,而是虚构的代码?我们怎么知道无论你编辑的代码是什么,都是真实的代码,而不是一些虚构的代码,所以没有人会浪费任何时间回答一个关于虚构代码的问题,这可能不适用于真正的代码?您是否熟悉 Stackoverflow 对最小可重现示例的要求?
1赞 JaMiT 6/25/2022
“I is getting an error” -- 我没有看到将错误消息复制到您的问题正文中,也没有看到哪一行触发错误的指示。(有时前者会处理后者,因为某些编译器会在错误消息中显示有问题的行,以及指向行中特定字符的箭头。
1赞 Pete Becker 6/25/2022
错误消息说的不仅仅是“错误:使用已删除的功能”,其余信息是关键部分。不要解释错误消息。

答:

1赞 Remy Lebeau 6/25/2022 #1

在 中,您将按值传递给构造函数。这需要制作 的副本,这是不可能的,因为它具有其复制构造函数,因此会出现错误。Child::function1()d_retriever_upProviderd_retriever_upunique_ptrdelete

您需要使用 to 移动到 .你声称正在这样做,但你没有,至少不是在所有需要它的地方。将调用方的参数移动到构造函数的参数中与将参数移动到 的类成员中是不同的操作。std::move()d_retriever_upProviderunique_ptrProviderretriever_upretriever_upProviderd_retriever

但是,在解决此问题之后,您将遇到另一个问题。 标记为,这使得访问位于该上下文中,因此无法修改 ,包括移动(因为移动 a 会将其持有指针设置为 )。Child::function1()constd_retriever_upconstfunction1()d_retriever_upunique_ptrnullptr

因此,要使代码正常工作,您需要删除 并添加:conststd::move()

T::Y Child::function1()
{
    Provider provider(d_impl, std::move(d_retriever_up));
    ...
    return Y;
}

话虽如此,移动在你的设计中是有问题的。您可能需要考虑改用。d_retriever_upstd::shared_ptr