2

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

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

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

  • Are there implementations of std containers for unique_ptr? In particular std::map.
  • How would a transparent interface be implemented?
Konrad Eisele
  • 3,088
  • 20
  • 35
  • I don't understand your question: what are you trying to do with the map and the wrapping? – JVApen Jan 16 '19 at 19:48
  • @JVApen : I want a map that destroys its items at the end/when overwriting. I’m using the raw pointer outside of the code. I want to get rid of the noise generated by having to wrap/unwrap when saving and also accessing. – Konrad Eisele Jan 16 '19 at 20:04
  • 1
    Why would you want to prefer raw pointers over the unique one? – JVApen Jan 16 '19 at 20:05
  • @JVApen: dont need unique pointers ouside of the map code. Lifecycle is managed in the map only. It would be ideal if c++ standard had a quailifier called 'unique' for pointers and C++ could do it transparently.. – Konrad Eisele Jan 16 '19 at 20:13

3 Answers3

10

Generally, we don't want to implicitly create std::unique_ptr because that can be dangerous.

In this sample, I'd recommend starting out with a unique_ptr rather than a bare new. This 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));
}

within insert you can also use std::move to transfer ownership to the collection.

Drew Hoskins
  • 4,168
  • 20
  • 23
3

When you pass a raw pointer into a function, it is generally unknown who is expected to keep ownership of the pointed-to object when the function exits - the caller or the callee. There is nothing in the language to specify or enforce that.

So, it is generally good practice to pass in a raw pointer only when the callee reads or modifies the object but the caller is expected to maintain ownership.

In your example, that is not the case. You want your functions to take ownership of the allocated method objects. So, you should change your functions to pass in std::unique_ptr<method> objects by value, instead of passing raw method* pointers. That makes it very explicit that ownership is expected to pass from the caller to the callee, eg:

#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

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

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

Why? This hides information from the reader. It is important to know whether something is dynamically allocated or not.


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

Not in the Standard Library.


How would a transparent interface be implemented?

Store a normal container inside your wrapper, provide forwarding functions that create unique_ptr where required. Iterators could unwrap automatically. E.g.

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)...));
    }
};
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416