【问题标题】:Copying std::unique_ptr's value via dereferencing通过取消引用复制 std::unique_ptr 的值
【发布时间】:2023-03-23 14:48:01
【问题描述】:

我编写了以下代码,尝试将unique_ptr 对象的值复制到一个结构中。

#include <iostream>
#include <memory>
using namespace std;

struct S {
    S(int X = 0, int Y = 0):x(X), y(Y){}

    // S(const S&) {}
    // S& operator=(const S&) { return *this; }

    int x;
    int y;
    std::unique_ptr<S> ptr;
};

int main() {
    S s;
    s.ptr = std::unique_ptr<S>(new S(1, 4));
    S p = *s.ptr; // Copy the pointer's value
    return 0;
}

它在 Visual C++ 2012 中弹出错误:

IntelliSense:没有合适的用户定义从“S”到“S”的转换 存在
IntelliSense:没有运算符“=”与这些操作数匹配 操作数类型为: std::unique_ptr> = std::unique_ptr>
错误 C2248:“std::unique_ptr<_ty>::unique_ptr”:无法访问 在类'std::unique_ptr<_ty>'中声明的私有成员

除非我取消注释我试图定义复制构造函数和 =operator 的行。 这消除了编译器错误,但没有消除 IntelliSense 错误。无论错误列表中显示的 IntelliSense 错误如何,它都会编译。

那么,为什么不能只使用默认函数并使用它们进行编译呢?我是否以正确的方式复制价值?如果需要,我应该如何定义复制构造函数?

【问题讨论】:

    标签: c++ c++11 unique-ptr visual-c++-2012


    【解决方案1】:

    复制构造函数不会隐式生成,因为您有一个用户定义的构造函数,这就是为什么您尝试复制 S 失败的原因。

    但是,unique_ptr 仍然不可复制,只能可移动,因此您可以为S 使用移动构造函数

    S(S&& other) : x(other.x), y(other.y), ptr(std::move(other.ptr))
    {
    
    }
    

    并称之为:

    S p = std::move(s); // Move s to p
    

    Live demo

    【讨论】:

    • 真的没有办法在不牺牲堆栈对象的情况下复制指针的值吗?我的意思是留下ptr的信息,只复制xy的值。
    • @cpx unique_ptr 的全部意义在于它是唯一的,因此没有其他对象可以写入指针的内存。如果您希望多个对象引用内存,则需要使用正确的类,可能是shared_ptr
    • 可以复制unique_ptr 指向的数据,并将构建该副本放入一个新的unique_ptr 中。我认为这也是完全合理的。原始指针仍然是“唯一的”,仍然是指向该原始对象的唯一指针。可以有另一个指向具有相同值的 不同 对象的指针。例如。 auto x1 = make_unique&lt;int&gt;(7); auto x2 = make_unique&lt;int&gt;(7); 但是,如果您尝试复制实际上指向Derivedunique_ptr&lt;Base&gt; 的值,它将导致切片(可能还有未定义的行为)。 (也有解决方法。)
    • (... 但我想我几分钟前的评论并不直接适用于这个问题。S 本身是不可复制的,因为它有一个unique_ptr 成员。如果@ 987654339@ 是可复制的,那么(在我看来)询问“深度复制”unique_ptr&lt;S&gt; 是合理的)
    【解决方案2】:

    std::unique_ptr 既不能复制构造也不能复制赋值。

    S 的隐式复制赋值运算符和构造函数格式不正确,因此会出现错误消息。

    但是,您可以使用 S p = std::move(s);,因为 std::unique_ptr 是可移动构造和可移动分配的,

    【讨论】:

    • 这个答案是正确的,但我认为问题中的循环设置。真正的问题是S 本身不是可复制构造或可复制分配的。 S 有问题的原因是因为它有一个 unique_ptr&lt;?&gt; 成员 - 该成员可能是 unique_ptr&lt;T&gt; 而不是 unique_ptr&lt;S&gt;,它仍然会导致同样的问题。
    【解决方案3】:

    不是一个完整的答案,仅供参考:

    我强烈建议您在实验中添加可见性:

    std::ostream&
    operator<<(std::ostream& os, const S& s)
    {
        os << '{' << s.x << ", " << s.y << ", ";
        if (s.ptr != nullptr)
            os << s.ptr.get() << ':' << *s.ptr;
        else
            os << "nullptr";
        return os << '}';
    }
    

    现在你可以这样说:

    cout << "s = " << s << '\n';
    

    在您的实验中的多个位置,并真正了解每一步之后发生的情况。这应该有助于您分析并继续您的设计。

    【讨论】:

      【解决方案4】:

      那么,为什么不能只使用默认函数并使用它们进行编译呢?

      据我了解,unique_ptr 容器背后的想法是它只处理其内容的生命周期(指向 T 的指针),直到被解除该职责(使用 swapreset方法),或者已经有效地销毁了它的内容(当它本身被销毁时)。 unique_ptr 的第二个重要属性是它必须允许 T 的不完整类型(以支持不透明指针)。这意味着包含的值可能不是CopyConstructible。因此,unique_ptr 本身不能被允许 CopyConstructible

      我是否以正确的方式复制价值? 如果需要,我应该如何定义复制构造函数?

      如果T 最终成为CopyConstructible,那么您必须通过访问指针手动处理副本,就像在main 中所做的那样。复制构造函数可能应该做同样的事情。

      【讨论】:

        猜你喜欢
        • 2023-03-23
        • 1970-01-01
        • 2015-12-20
        • 2019-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-03
        相关资源
        最近更新 更多