如何使用move构造函数正确处理shared_ptr的映射



考虑我有一个容器std::map<int, std::shared_ptr<MyClass>>,我想在外部函数中填充它,避免处理它的内容。所以我有

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.
}

如果我使用传统的复制构造函数,一切都如预期的那样工作。

这太复杂了。为什么不这么说呢:

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

如果您绝对必须使用helper函数,那么您必须返回一个对象,而不是一个悬空引用。类似这样的东西:

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

很难再放纵任何比这更普通的东西了,但最后一个永远不会在家里使用的版本:

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;
}

问题是您正在从f()返回引用。右值引用仍然是一个引用,就像你永远不会从函数返回对局部的引用一样,你也不能用右值引用来做这件事。

好消息是,你不必这么做。由于返回值优化,你可以简单地从f()返回Container,它将完全满足你的需求。

最新更新