Есть ли прозрачный способ использования unique_ptr в контейнерах std? - PullRequest
0 голосов
/ 16 января 2019

Есть ли прозрачный способ использования 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; и т. Д. Вместо необходимости оборачивать / разворачивать для каждого доступа.

  • Существуют ли реализации контейнеров std для unique_ptr? В частности std::map.
  • Как будет реализован прозрачный интерфейс?

Ответы [ 3 ]

0 голосов
/ 16 января 2019

Как правило, мы не хотим неявно создавать std::unique_ptr, потому что , что может быть опасно .

В этом примере я бы рекомендовал начинать с unique_ptrчем голый new.Это гарантирует, что весь срок жизни отслеживается .

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

в insert, вы также можете использовать std::move для передачи права собственности на коллекцию.

0 голосов
/ 17 января 2019

Когда вы передаете необработанный указатель в функцию, обычно неизвестно, кто, как ожидается, сохранит владение указанным объектом при выходе из функции - вызывающий или вызываемый. В языке нет ничего, чтобы указывать или применять это.

Таким образом, обычно рекомендуется передавать необработанный указатель только в том случае, если вызываемый объект считывает или изменяет объект, но ожидается, что вызывающий объект сохранит владение.

В вашем примере это не так. Вы хотите, чтобы ваши функции стали владельцем выделенных 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;                                                                                                                               
}

Живая демоверсия

0 голосов
/ 16 января 2019

Я бы хотел использовать tbl.insert({id,m}); и tbl[id] = m; вместо необходимости оборачивать / разворачивать для каждого доступа.

Почему? Это скрывает информацию от читателя. Важно знать, распределяется ли что-то динамически или нет.


Существуют ли реализации контейнеров std для unique_ptr? В частности std::map.

Нет в стандартной библиотеке.


Как будет реализован прозрачный интерфейс?

Храните обычный контейнер внутри вашей оболочки, предоставьте функции пересылки, которые создают 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)...));
    }
};
...