在 std 容器中有使用 unique_ptr 的透明方法吗?

Is there a transparent way of using unique_ptr in std containers?

是否有在容器中使用 std::unique_ptr 的透明方法?

#include <iostream>                                                                                                                         
#include <memory>                                                                                                                           
#include <map>                                                                                                                              

struct method {                                                                                                                             
    virtual ~method() { std::cout << "f\n"; };                                                                                              
};                                                                                                                                          
typedef std::unique_ptr<method> MPTR;                                                                                                       

std::map<int, MPTR> tbl;                                                                                                                    

void insert(int id, method *m) {                                                                                                            
    tbl.insert({id,std::unique_ptr<method>(m)});                                                                                            
};                                                                                                                                          

void set(int id, method *m) {                                                                                                               
    tbl[id] = std::unique_ptr<method>(m);                                                                                                   
};                                                                                                                                          

int main(int argc, char **argv) {                                                                                                           

    insert(1,new method());                                                                                                                 
    set(1,new method());                                                                                                                    
    return 0;                                                                                                                               
}   

我想使用 tbl.insert({id,m});tbl[id] = m; 等,而不是每次访问都必须 wrap/unwrap。

I'd like to use tbl.insert({id,m}); and tbl[id] = m; instead of having to wrap/unwrap for each access.

为什么?这隐藏了来自 reader 的信息。重要的是要知道某些东西是否是动态分配的。


Are there implementations of std containers for unique_ptr? In particular std::map.

不在标准库中。


How would a transparent interface be implemented?

在包装器中存储一个普通容器,提供在需要时创建 unique_ptr 的转发功能。迭代器可以自动展开。例如

template <typename T>
class unique_ptr_vector
{
    std::vector<std::unique_ptr<T>> _data;

public:
    template <typename... Ts>
    void emplace_back(Ts&&... xs)
    {
        _data.emplace_back(std::make_unique<T>(std::forward<Ts>(xs)...));
    }
};

通常,我们不想隐式创建 std::unique_ptr 因为 that can be dangerous.

在此示例中,我建议从 unique_ptr 而不是裸 new 开始。这个makes sure the entire lifetime is tracked.

#include <memory>
int main(int argc, char **argv) {
    auto m = std::make_unique<method>();
    insert(1, std::move(m));
}

insert 内,您还可以使用 std::move 将所有权转让给 collection。

当您将原始指针传递给函数时,通常不知道在函数退出时谁应该保留所指向对象的所有权 - 调用者或被调用者。语言中没有任何内容可以指定或强制执行。

因此,通常好的做法是仅在被调用方读取或修改对象但调用方应保持所有权时才传入原始指针。

在您的示例中,情况并非如此。您希望您的函数获得分配的 method 对象的所有权。因此,您应该更改函数以按值传递 std::unique_ptr<method> 对象,而不是传递原始 method* 指针。这使得所有权应该从调用者传递给被调用者变得非常明确,例如:

#include <iostream>
#include <memory>
#include <map>

struct method {
    virtual ~method() { std::cout << "f\n"; };
};
typedef std::unique_ptr<method> MPTR;

std::map<int, MPTR> tbl;

void insert(int id, MPTR m) {
    tbl.insert(std::make_pair(id, std::move(m)));
};

void set(int id, MPTR m) {
    tbl[id] = std::move(m);
};

int main()
{
    insert(1, MPTR(new method)); // or insert(1, std:::make_unique<method>()) in C++14 and later
    set(1, MPTR(new method)); // or set(1, std:::make_unique<method>()) in C++14 and later
    return 0;                                                                                                                               
}

Live demo