高效优雅地返回安置unique_ptr

Efficiently and elegantly returning emplaced unique_ptr

本文关键字:unique ptr 返回 高效      更新时间:2023-10-16

我发现(感谢StackOverflow注释)我的代码中的一个安全漏洞:

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

基本上,如果emplace_back引发,使用原始new分配Item可能会泄漏内存。

解决方案绝不是使用原始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是无效的,因为我必须使用std::move将其放入items容器中item移动。

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

有没有更优雅的方式来返回被移动以放置在容器中的std::unique_ptr

你可以

这样写:

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

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

您的问题被标记为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;
}

这应该是安全的,因为在构建unique_ptr<Item>之前无法调用emplace_back(),因此即使emplace_back()确实抛出,您的Item也已经由unique_ptr管理。