可以将右值移动到 shared_ptr 中吗

Posted

技术标签:

【中文标题】可以将右值移动到 shared_ptr 中吗【英文标题】:Can an rvalue be moved into a shared_ptr 【发布时间】:2016-02-24 13:23:09 【问题描述】:

是否可以将rvalue“移动”到shared_ptr。到目前为止我尝试过的所有方法都会产生副本。

我想要的使用模式是:

class Element 
public:
    Element(const string &);
    Element(const Element &) = delete; //delete copy constructor
    // ...
;

class MyCollectionOfElements 
public:
    void add(Element &&); // add by rvalue
    // ...
protected:
    vector<shared_ptr<Element>> elements;
;

MyCollectionOfElements elements;
elements.add(Element("something"));

我想要这种模式是有原因的,并且了解有一些自然有效的替代模式(例如,将新的 Element 作为指针而不是 rvalue 传递)。

我目前的怀疑是传入的rvalue 在堆栈上,而我需要将它放在堆上以将其存储在shared_ptr 中,因此副本是不可避免的。

【问题讨论】:

C++ 中没有“堆栈”或“堆”(std::stackstd::make_heap 除外)。 我认为他指的是程序的stack 和heap,它们在C++ 中绝对存在 @Conduit:C++ 需要引用。 它们是编程语言的主要内容。当您分配变量、将它们传递给函数或使用内存进行任何操作时,数据将存储在这两种结构之一中。 I'll just leave this here 并在路上。 【参考方案1】:

是的:

void add(Element && e)

    elements.push_back(std::make_shared<Element>(std::move(e)));

你还必须确保你的类实际上一个移动构造函数:

class Element

public:
    Element(Element &&) = default;
    // ...
;

通过声明一个复制构造函数,你禁止了一个移动构造函数的隐式声明,所以你需要显式声明它。

【讨论】:

谢谢你,Kerrek。这在 Visual Studio 中不像我预期的那样工作。除非我不删除复制构造函数,否则这不会编译。当它返回复制构造函数时,我可以看到在 make_shared(std::move(e)) 之后内存地址发生了变化,因此 Element 的 lambda 函数中的this 仍然无效。 @ElliotWoods:您的问题并未说明您的 this 指针应该保持不变——您应该在问题中澄清这一点。可能无法避免更改 this 指针(但这是移动与复制之外的另一个问题)。 @ElliotWoods:这不是“移动”通常的意思。你永远不能“移动”一个实际的变量。 “移动”总是指移动变量的。提供的代码是您应该想要的。 @KerrekSB - 谢谢。你能想象为什么我可能需要在 Visual Studio 中允许复制构造函数才能让你的方法工作吗? @ElliotWoods:我不太明白你的问题。您能否将问题简化为尽可能小的形式并将其作为单独的问题发布?【参考方案2】:

猜你误解了运动语义的概念。您必须将指针传递给智能指针,并且指针可以具有任何类型 - 左值/右值

    void f(A *&&p) 

接受右值引用指向指向A的指针的函数;但是 p 仍然是左值,它具有对指针的 r 值引用类型, 所以你必须“cast”(std::move - 什么都不做,只是将 l-value 转换为 r-value) std::shared_ptr(std::move(p));

    A *g()  return new A;
    A *a;
    f(a);//error        
    f(g());//works fine
    f(new A());//also fine

【讨论】:

谢谢 jonezq,但问题是如何(如果可能)从Element &amp;&amp;(不是Element * &amp;&amp;)创建shared_ptr&lt;Element&gt;,而不导致Element 的副本 您可以获取右值对象的地址并将其传递给 shared_ptr,然后您就可以离开函数/方法范围 - 对象被销毁,并且当 shared_ptr 递减 ref 计数器为零时也会调用析构函数被破坏的对象(意外行为)。

以上是关于可以将右值移动到 shared_ptr 中吗的主要内容,如果未能解决你的问题,请参考以下文章

将右值引用绑定到(自动生成的)左值

函数不将右值引用作为参数? [复制]

拷贝控制3(对象移动)

对象移动

将右值引用分配给左值时发生了啥?

将右值按值传递给函数时,为啥不调用复制构造函数