This may be a bit implementation-specific, but some of it seems fundamental.
I'm certain I must be missing something in the standard library.
The problem is this:
I want to implement a std::unique_ptr
whose deleter is free()
[ because the value is allocated via malloc()
]
There are, of course, lots of options on how to do this, but (at least in g++ 4.8.4 for x86-64) they appear to have different memory usage implications.
E.g.: Method 1:
std::unique_ptr<char, std::function<void(void*)>> ptr_a(malloc(10), free);
However,
sizeof(ptr_a)
== 40 bytes (8 for void*, 32 for std::function<>)
Method 2:
std::unique_ptr<void, void (*)(void*)> ptr_b(malloc(10), free);
Somewhat better, as
sizeof(ptr_b)
== 16 bytes (8 for void*, 8 for bare function pointer ])
Method 3:
template <void (*T)(void*)>
class Caller {
public:
void operator()(void* arg) {
return T(arg);
}
};
std::unique_ptr<void, Caller<free>> ptr_c(malloc(10));`
At this point, sizeof(ptr_c)
== 8 bytes (the minimum possible) - but I've had to introduce a class that's pretty much pure boilerplate (and, as shown, easily templatized).
This seems like such a simple pattern - is there some element in the STL that does what Caller<>
does above?
Of course, g++ by default does indeed appear to free() when calling delete on a trivial type - but this seems far from guaranteed by the standard (if nothing else, new/delete may be redefined from the default allocation/deallocation functions, and default_delete would then call the replacement delete).
And plus, there are other cases where the release of some object allocated in a pure-C library would be implemented by a simple function call, rather than a deleter. It seems somewhat tedious to have to wrap such allocation/deallocation functions in classes in order to get std::unique_ptr to call them both correctly and efficiently - which makes me think I'm missing something (most of the rest of the modern C++ specs seem to be extremely well thought out).
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.
In C++11 we can transfer the ownership of an object to another unique_ptr using std::move() . After the ownership transfer, the smart pointer that ceded the ownership becomes null and get() returns nullptr.
A unique_ptr object wraps around a raw pointer and its responsible for its lifetime. When this object is destructed then in its destructor it deletes the associated raw pointer. unique_ptr has its -> and * operator overloaded, so it can be used similar to normal pointer.
std::default_delete is the default destruction policy used by std::unique_ptr when no deleter is specified. Specializations of default_delete are empty classes on typical implementations, and used in the empty base class optimization.
C++11 has an integral_constant
type that works on things that aren't integer-like. In C++14, there is a constexpr
cast back to the value.
So, in C++14, we can do:
std::unique_ptr<void, std::integral_constant<decltype(free)*, free>> ptr_c(malloc(10));
this is awkward. (This relies on the fact that ()
will consider cast-to-function-pointer on its left hand side argument).
We can hard code free as:
using default_free = std::integral_constant<decltype(free)*, free>;
std::unique_ptr<void, default_free> ptr_c(malloc(10));
to get rid of some noise at the use site.
In C++17, we can write a helper:
template<auto t>
using val = std::integral_constant< std::decay_t<decltype(t)>, t >;
giving us:
std::unique_ptr<void, val<free>> ptr_c(malloc(10));
which is cleaner in my opinion.
Live examples.
We can write a our own version in C++11:
template<class T, std::decay_t<T> t>
struct val {
constexpr operator T() noexcept const { return t; }
};
using default_free = val<decltype(free), free>;
std::unique_ptr<void, default_free> ptr_c(malloc(10));
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