Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a std::unordered_set of std::unique_ptr

Assume I have a set of unique_ptr:

std::unordered_set <std::unique_ptr <MyClass>> my_set; 

I'm not sure what's the safe way to check if a given pointer exists in the set. The normal way to do it may be to call my_set.find (), but what do I pass as a parameter?

All I have from the outside is a raw pointer. So I have to create another unique_ptr from the pointer, pass it to find() and then release() that pointer, otherwise the object would get destructed (twice). Of course, this process can be done in a function, so the caller can pass the raw pointer and I do the conversions.

Is this method safe? Is there a better way to work with a set of unique_ptr?

like image 664
cfa45ca55111016ee9269f0a52e771 Avatar asked Jul 25 '13 06:07

cfa45ca55111016ee9269f0a52e771


People also ask

What is the use of std :: Unique_ptr?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

In what situation is a Shared_ptr more appropriate than a Unique_ptr?

Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.

What is the use of std :: Make_unique?

make_unique prevents the unspecified-evaluation-order leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)) . (Following the advice "never say new " is simpler than "never say new , unless you immediately give it to a named unique_ptr ".)


2 Answers

You can also use a deleter that optionally doesn't do anything.

template<class T> struct maybe_deleter{   bool _delete;   explicit maybe_deleter(bool doit = true) : _delete(doit){}    void operator()(T* p) const{     if(_delete) delete p;   } };  template<class T> using set_unique_ptr = std::unique_ptr<T, maybe_deleter<T>>;  template<class T> set_unique_ptr<T> make_find_ptr(T* raw){     return set_unique_ptr<T>(raw, maybe_deleter<T>(false)); }  // ...  int* raw = new int(42); std::unordered_set<set_unique_ptr<int>> myset; myset.insert(set_unique_ptr<int>(raw));  auto it = myset.find(make_find_ptr(raw)); 

Live example.

like image 198
Xeo Avatar answered Oct 02 '22 16:10

Xeo


Note that the ability to do heterogenous lookups on standard containers is subject of some proposals.

http://cplusplus.github.io/LWG/lwg-proposal-status.html lists

  • N3465 Adding heterogeneous comparison lookup to associative containers for TR2 (Rev 2) [Handle with N3573]
  • N2882 id.
  • N3573 Heterogenous extensions to unordered containers [Handle with N3465]

Especially the latter looks like it would cover your use case.

For now, here is an IMO not very pretty but working alternative workaround (O(n)):

#include <iterator> #include <iostream> #include <algorithm>  #include <unordered_set> #include <memory>  #include <cassert>  struct MyClass {};  template <typename T> struct RawEqualTo {     RawEqualTo(T const* raw) : raw(raw) {}      bool operator()(T const* p) const           { return raw == p; }     bool operator()(std::unique_ptr<T> const& up) const           { return raw == up.get(); }    private:     T const* raw; };   using namespace std; int main() {     std::unordered_set <std::unique_ptr <MyClass>> my_set;      my_set.insert(std::unique_ptr<MyClass>(new MyClass));     my_set.insert(std::unique_ptr<MyClass>(new MyClass));      auto raw = my_set.begin()->get();      bool found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));     assert(found);      raw = new MyClass;      found = end(my_set) != std::find_if(begin(my_set), end(my_set), RawEqualTo<MyClass>(raw));     assert(!found);      delete raw; } 

Warning It's also very inefficient, of course.

like image 40
sehe Avatar answered Oct 02 '22 14:10

sehe