Raw pointer lookup for sets of unique_ptrs
In C++14, std::set<Key>::find
is a template
function if Compare::is_transparent
exists. The type you pass in does not need to be Key
, just equivalent under your comparator.
So write a comparator:
template<class T>
struct pointer_comp {
typedef std::true_type is_transparent;
// helper does some magic in order to reduce the number of
// pairs of types we need to know how to compare: it turns
// everything into a pointer, and then uses `std::less<T*>`
// to do the comparison:
struct helper {
T* ptr;
helper():ptr(nullptr) {}
helper(helper const&) = default;
helper(T* p):ptr(p) {}
template<class U, class...Ts>
helper( std::shared_ptr<U,Ts...> const& sp ):ptr(sp.get()) {}
template<class U, class...Ts>
helper( std::unique_ptr<U, Ts...> const& up ):ptr(up.get()) {}
// && optional: enforces rvalue use only
bool operator<( helper o ) const {
return std::less<T*>()( ptr, o.ptr );
}
};
// without helper, we would need 2^n different overloads, where
// n is the number of types we want to support (so, 8 with
// raw pointers, unique pointers, and shared pointers). That
// seems silly:
// && helps enforce rvalue use only
bool operator()( helper const&& lhs, helper const&& rhs ) const {
return lhs < rhs;
}
};
then use it:
typedef std::set< std::unique_ptr<Foo>, pointer_comp<Foo> > owning_foo_set;
now, owning_foo_set::find
will accept unique_ptr<Foo>
or Foo*
or shared_ptr<Foo>
(or any derived class of Foo
) and find the correct element.
Outside of C++14, you are forced to use the map
to unique_ptr
approach, or something equivalent, as the signature of find
is overly restrictive. Or write your own set
equivalent.
Searching a set of unique pointers
You can use std::find_if
like this:std::find_if(numbers.begin(), numbers.end(), [&](std::unique_ptr<int>& p) { return p.get() == x;});
Is unique_ptr constructor initializes the raw pointer and also unique_ptr destructor deletes the associated raw pointer?
When creating unique_ptr object "p1" we are providing raw pointer. Internally, unique_ptr constructor will initialize the unique_ptr with the raw pointer. Is my understanding correct?
Yes. The unique pointer will hold the same address.
As per the unique_ptr definition, "The pointer is exclusively owned by one object or a resource".
Based on the above statement, in our scenario, "raw pointer" is exclusively owned by the unique_ptr object "p1". Am I correct?
Yes. The only reference, the one that owns the resource and will free it, is the unique pointer. Note however that it's not the pointer that's owned, but the object it points at. The unique_ptr didn't take ownership of the raw pointer, it took ownership of the object (the resource) that is at the address the raw pointer provided.
And also after the statement, cout << p1.get(); (In the above sample program) as it is going out of scope, internally, the destructor of the unique_ptr called and it deletes the associated raw pointer. Is my understanding correct?
Yes. The unique ptr will cause the deletion of its internal raw pointer when it goes out of scope.
Finally, once deletes the associated raw pointer is the unique_ptr object will become empty?
Doesn't have to. Since the deletion happens when the unique_ptr
object itself is being destroyed, there is no real need to "empty" it. It's about to go out of existence anyway, so its value is immaterial.
Using std::less for unique_ptr to raw pointer comparison fails to compile, but works with wrapper?
Neither std::unique_ptr<int>
nor int
involve user-defined types; in terms of ADL their associated namespaces are namespace std
only.
When you use a wrapper type you have an associated namespace of the root namespace (the namespace where you declared the wrapper), which means that ADL can find the free operator<
s defined there.
Indeed, this is a Good Thing since it prevents someone else writing their own operator<(int*, std::unique_ptr<int> const&)
which might have a different behavior to yours.
why the raw pointer get by std::unique_ptr's get() can not delete the object and how is that implemented
std::unique_ptr
is a really simple class. Conceptually, it's basically just this:
template <typename T>
class unique_ptr
{
private:
T* ptr;
public:
unique_ptr(T* p) ptr{p} {}
~unique_ptr() { delete ptr; }
T* get() { return ptr; }
T* release() {
T* p = ptr;
ptr = nullptr;
return p;
}
// other stuff
};
It just has a pointer member, and delete
s the pointed-to object in its destructor. In reality there's a bit more to it, but that's essentially it.
The get
member just returns a copy of the unique_ptr
's managed pointer. That's it. Since the unique_ptr
's pointer member is still pointing to that object, its destructor will still delete
the object. If you also delete
that object via another pointer to it then it will get delete
d twice, which results in undefined behavior.
The release
member function, on the other hand, sets the unique_ptr
's pointer member to nullptr
before returning a copy of its original value. Since its member pointer is null, its destructor won't delete anything.
How to convert a function that returns unique_ptr into raw pointer?
A lambda function should work, e.g.:
LegacyFunction([](float a, float b) { return MyFunc(a, b).release(); }, 1, 2.0f);
but only if LegacyFunction
will naturally delete/free the memory that MyFunc
allocated with the appropriate operation (so if MyFunc
used new int
, LegacyFunction
must use delete retval;
; if it used new int[x]
, it must use delete[] retval;
).
The release
method gets the contained pointer and relinquishes ownership, so you've explicitly given up unique_ptr
's protections; not a great idea if you can avoid it.
Map of unique pointers, .at() with a raw pointer
In C++ 14, you can provide a transparent comparator
template<typename T>
struct PtrCompare
{
std::less<T*> less;
using is_transparent = void;
bool operator()(T* lhs, const std::unique_ptr<T> & rhs) const { return less(lhs, rhs.get()); }
bool operator()(const std::unique_ptr<T> & lhs, T* rhs) const { return less(lhs.get(), rhs); }
bool operator()(const std::unique_ptr<T> & lhs, const std::unique_ptr<T> & rhs) const { return less(lhs.get(), rhs.get()); }
}
std::map<std::unique_ptr<SomeType>, SomeOtherType, PtrCompare<SomeType>> map;
That doesn't help with at
, but does allow you to find
based on anything that you can compare
SomeType* p = ...;
if (auto it = map.find(p))
{
// use it->second
}
else
{
throw std::out_of_range;
}
Related Topics
How to Check If a Function Exists in C/C++
Is Boost Shared_Ptr <Xxx> Thread Safe
Broken C++ Std Libraries on MACos High Sierra 10.13
Are Global Variables in C++ Stored on the Stack, Heap or Neither of Them
Calling a Function Through Its Address in Memory in C/C++
What Is a Seed in Terms of Generating a Random Number
C++ View Types: Pass by Const& or by Value
Of Memory Management, Heap Corruption, and C++
C++11 Variable Number of Arguments, Same Specific Type
Qtcpsocket: Reading and Writing
Show Two Digits After Decimal Point in C++
How to Force Gcc to Assume That a Floating-Point Expression Is Non-Negative
_Attribute_((Weak)) and Static Libraries
How Are the _Cplusplus Directive Defined in Various Compilers
Using Std::Move() When Returning a Value from a Function to Avoid to Copy
Does Std::Mutex Create a Fence
Why Isn't Rvo Applied to Base Class Subobject Initialization