11

Is it possible to 'move' an rvalue into a shared_ptr. All the methods which I've tried so far result in a copy.

My desired usage pattern is:

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"));

There are reasons why I want this pattern, and understand that there are alternative patterns which naturally work (e.g. passing the new Element as a pointer rather than an rvalue).

My current suspicion is that the incoming rvalue is on the stack, whilst I need to have it on the heap to store it in a shared_ptr, therefore a copy is inevitable.

Elliot Woods
  • 834
  • 11
  • 20
  • There's no "stack" or "heap" in C++ (except for `std::stack` and `std::make_heap`). – Kerrek SB Feb 24 '16 at 13:26
  • 8
    I think he's referring to the program's [stack](https://en.wikipedia.org/wiki/Call_stack) and [heap](https://en.wikipedia.org/wiki/Memory_management#HEAP), which absolutely do exist in C++ – Conduit Feb 24 '16 at 13:30
  • 1
    @Conduit: Citation needed for C++. – Kerrek SB Feb 24 '16 at 13:31
  • 5
    They're a staple of programming languages. When you allocate variables, pass them to functions, or do *literally anything with memory*, the data is stored in one of those two structures. – Conduit Feb 24 '16 at 13:32
  • @Conduit: None of this has anything to do with C++. – Kerrek SB Feb 24 '16 at 13:33
  • 1
    [I'll just leave this here](http://stackoverflow.com/questions/79923/what-and-where-are-the-stack-and-heap) and be on my way. – Conduit Feb 24 '16 at 13:34
  • @KerrekSB Would it perhaps be more correct to use the terms static memory vs dynamic memory? – default Feb 24 '16 at 13:47
  • @Default: Kind of. "Storage duration" is the most relevant concept in C++ in that direction. (It still doesn't have anything to do with this question.) "Scope" is also related and probably a lot more important. – Kerrek SB Feb 24 '16 at 14:07

2 Answers2

16

Yes:

void add(Element && e)
{
    elements.push_back(std::make_shared<Element>(std::move(e)));
}

You also have to make sure your class actually has a move constructor:

class Element
{
public:
    Element(Element &&) = default;
    // ...
};

By declaring a copy constructor, you inhibit the implicit declaration of a move constructor, so you need to declare it explicitly.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Thank you Kerrek. This doesn't work as I expected in Visual Studio. This won't compile unless I don't delete the copy constructor. When it has the copy constructor back, I can see that the memory address changes after the make_shared(std::move(e)), so `this` inside Element's lambda functions are still invalidated. – Elliot Woods Feb 24 '16 at 13:45
  • 3
    @ElliotWoods: Your question does not state that you have `this` pointers that should be unchanged -- you should clarify this in the question. It is probably not possible to avoid changing the `this` pointer (but that is another issue than move vs. copy). – Mathias Rav Feb 24 '16 at 13:53
  • 1
    @ElliotWoods: That's not what "moving" usually means. You can never "move" an actual variable. "Moving" always refers to moving a variable's *value*. The presented code is what you should want. – Kerrek SB Feb 24 '16 at 14:08
  • @KerrekSB - thank you. can you imagine why I might need to allow the copy constructor in Visual Studio for your method to work? – Elliot Woods Feb 24 '16 at 14:20
  • @ElliotWoods: I don't quite understand your question. Can you reduce the problem to its smallest possible form and post it as a separate question? – Kerrek SB Feb 24 '16 at 15:53
  • @KerrekSB : I've made a simple test of your solution at http://goo.gl/NIymiO which does work and compiles. Thank you. I had hoped it would mean `this` doesn't change, but that seems to be a separate issue as mentioned. – Elliot Woods Feb 24 '16 at 20:22
0

Guess you misunderstand the idea of movement semantic. You have to pass pointer to smart pointer, and pointer can have any type - lvalue/rvalue

    void f(A *&&p) {}

function which accept rvalue ref to pointer which points to A; but p is still lvalue which has type r-value reference to a pointer, so u have to "cast"(std::move - does nothing just cast l-value to 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
  • 344
  • 1
  • 5
  • 1
    Thank you jonezq, but the question is how (if possible) to create a `shared_ptr` from `Element &&` (not `Element * &&`), without causing a copy of the `Element` – Elliot Woods Feb 24 '16 at 13:50
  • You can take address of rvalue object and to pass it to shared_ptr<>, then you get out of function/method scope - object gets destroyed, and when shared_ptr decrements ref counter to zero will also call destructor of destroyed object (unexpected behavior). – jonezq Feb 24 '16 at 13:56