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?
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.
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.
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 ".)
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With