高效优雅地返回emplaced unique_ptr

时间:2014-02-20 17:21:55

标签: c++ c++11 return unique-ptr emplace

我在代码中发现了(thanks to a StackOverflow comment)安全漏洞:

std::vector<std::unique_ptr<Item>> items;

template<class... TS> Item& create(TS&&... mArgs)
{
    auto item(new Item(std::forward<TS>(mArgs)...);
    items.emplace_back(item); // Possible exception and memory leak
    return *item;
}

基本上,如果Item抛出,则new分配原始emplace_back可能会泄漏内存。

解决方案永远不会使用原始new,而是在方法正文中使用std::unique_ptr权限。

std::vector<std::unique_ptr<Item>> items;

template<class... TS> Item& create(TS&&... mArgs)
{
    auto item(std::make_unique<Item>(std::forward<TS>(mArgs)...);
    items.emplace_back(std::move(item));
    return *item; // `item` was moved, this is invalid!
}

正如您所看到的,返回item无效,因为我必须使用item移动std::move以将其置于items容器中。

我想不出一个需要在另一个变量中存储item的地址的解决方案。然而,原始(错误)解决方案非常简洁易读。

是否有一种更优雅的方式可以返回移动到容器中的std::unique_ptr

3 个答案:

答案 0 :(得分:8)

你可以写:

template<class... TS>
Item& create(TS&&... mArgs)
{
    items.emplace_back(std::make_unique<Item>(std::forward<TS>(mArgs)...));
    return *items.back();
}

答案 1 :(得分:2)

在emplace之前缓存引用是一个更普遍适用的选项(例如,对于非向量容器):

template<class... TS> Item& create(TS&&... mArgs)
{
    auto item = std::make_unique<Item>(std::forward<TS>(mArgs)...);
    auto& foo = *item;
    items.emplace_back(std::move(item));
    return foo; // This *is* valid.
}

答案 2 :(得分:2)

您的问题标记为C ++ 11,其他答案表明make_unique未提及它是C ++ 14功能。我相信这种C ++ 11方法也可以解决泄漏问题。

#include <vector>
#include <memory>
#include <utility>
#include <iostream>

struct Item
{
   int a, b;
   Item(int aa, int bb) : a{aa}, b{bb} { }
};

static std::vector<std::unique_ptr<Item>> items;

template <class... Ts> Item& create(Ts&&... args)
{
    items.emplace_back(std::unique_ptr<Item>{new Item(std::forward<Ts>(args)...)});
    return *items.back();
}

int main()
{
    Item& x = create(1, 2);
    std::cout << "( " << x.a << ", " << x.b << " )" << std::endl;
}

这应该是安全的,因为在emplace_back()已经构建之前无法调用unique_ptr<Item>,所以即使emplace_back()投出,Item已经由unique_ptr管理{1}}。