galadog
galadog

Reputation: 1000

How to handle a map of shared_ptr with move constructor correctly?

Consider I have a container std::map<int, std::shared_ptr<MyClass>> and I want to fill it in external function and avoid coping of its contents. So I have

typedef Container std::map<int, std::shared_ptr<MyClass>>

Container&& f(){
    Container bar;
    auto foo = std::shared_ptr<MyClass>(new MyClass());
    bar.insert(std::make_pair(0,foo));
    std::cout<<bar.at(1)->print_smth<<'\n'; //This works
    return std::move(bar);
}

int main(){
    Container baz(f());
    std::cout<<bar.at(1)->print_smth<<'\n'; //This doesn't
    // Container baz has element 1, but shared_ptr is invalidated, because it has 0 references.

}

If I use conventional copy constructor everything works as expected.

Upvotes: 4

Views: 6124

Answers (2)

Fozi
Fozi

Reputation: 5135

The problem is that you are returning a reference from your f(). An rvalue reference is still a reference and just like you would never return a reference to a local from a function you can't do that with rvalue references either.

The good news is that you don't have to. Thanks to the return value optimization you can simply return a Container from f() and it will do exactly what you are looking for.

Upvotes: 0

Kerrek SB
Kerrek SB

Reputation: 476990

This is far too complicated. Why not just say this:

int main()
{
    Container baz { { 0, std::make_shared<MyClass>() } };

    // ...
}

If you absolutely must go with the helper function, you have to return an object, not a dangling reference. Something like this:

Container f()
{
    return Container { { 0, std::make_shared<MyClass>() } };
}

It's hard to indulge anything more pedestrian than this, but one final, never-to-be-used-at-home version:

Container f()
{
   Container bar;
   auto p = std::make_shared<MyClass>;

   bar[0] = p;                        // Method #1
   // ---- ALTERNATIVELY ---
   bar.insert(std::make_pair(0, p));  // Method #2
   // ---- ALTERNATIVELY ---
   bar.emplace(0, p);                 // Method #3

   return bar;
}

Upvotes: 6

Related Questions