Reputation: 2562
In "C++ Primer, 5th Edition", it is said that the type of the deleter of a shared_ptr
is not known until runtime, because the deleter is not stored directly as a member, but as a pointer that can point to a deleter. The type of the deleter in a unique_ptr
is known at compile-time, because it is a part of the unique_ptr
itself.
So, I've made this example:
#include <functional>
template <typename T>
struct SharedPtr
{
void(*pDel_)(T*) = nullptr;
T* ptr_{ new T{} };
~SharedPtr(){ pDel_ ? pDel_(ptr_) : delete ptr_; }
};
template <typename T, typename D = std::function<void(T*)>>
struct UniquePtr
{
D pDel_;
T* ptr_{ nullptr };
UniquePtr(T* p = nullptr, D del = D{}) :
ptr_(p), pDel_(del){}
~UniquePtr(){ pDel_(ptr_); }
};
int main()
{
SharedPtr<int> spi{};
cout << *spi.ptr_ << endl;
UniquePtr<std::string> upd{new std::string("Helo!"),
[](std::string* p){std::cout << "freeing memory...\n"; delete p; }};
}
in my opinion, the type of the deleter in SharedPtr
is known at compile time (void(*)(T*)
) but the value is not.
on the other hand, the type of the deleter in UniquePtr
is really known at compile time too, but again the value may not be.
So the book said that:
// del bound at compile time; direct call to the deleter is instantiated
del(p); // no run-time overhead
The type of del is either the default deleter type or a user-supplied type. It doesn’t
matter; either way the code that will be executed is known at compile time. Indeed, if
the deleter is something like our DebugDelete class (§ 16.1.4, p. 672) this call might
even be inlined at compile time.
By binding the deleter at compile time, unique_ptr avoids the run-time cost of an
indirect call to its deleter. By binding the deleter at run time, shared_ptr makes it
easier for users to override the deleter.
If the deleter in UniquePtr
is a pointer to a function, but that pointer is nullptr
, then del(p)
is undefined.
Please help me to understand this paragraph. I've implemented my shared_ptr
and unique_ptr
as a matter of practice only.
Upvotes: 3
Views: 140
Reputation: 238381
in my opinion, the type of the deleter in SharedPtr is known at compile time (
void(*)(T*)
) but the value is not.
That is true of your SharedPtr.
That is not true of std::shared_ptr.
on the other hand, the type of the deleter in UniquePtr is really known at compile time too, but again the value may not be.
When you instantiate UniquePtr with std::function<void(T*)>
, you would only know that type at compile time, but not the type of the function object that is wrapped by it.
std::unique_ptr does not use std::function deleter by default.
If the deleter in UniquePtr is a pointer to a function, but that pointer is nullptr, then del(p) is undefined.
This is true. Don't do that.
Please help me to understand this paragraph.
Just because a unique pointer can have a deleter known at compile time, doesn't mean that it has to have such deleter. A function wrapper is not nor is function pointer a compile-time deleter because they have runtime state.
Use a stateless deleter like for example std::default_delete<T>
(or perhaps DebugDelete mentioned in the quote which presumably is also stateless) to get compile time benefits.
Upvotes: 4