Reputation: 703
Scott Meyer’s “Effective Modern C++” discusses the use of std::unique_ptr
with custom deleter and states:
Deleters that are function pointers generally cause the size of a
std::unique_ptr
to grow from one word to two. For deleters that are function objects, the change in size depends on how much state is stored in the function object. Stateless function objects (e.g., from lambda expressions with no captures) incur no size penalty, and this means that when a custom deleter can be implemented as either a function or a captureless lambda expression, the lambda is preferable.
As an example, this:
auto delInvmt1 = [](Investment* pInvestment) {
makeLogEntry(pInvestment);
delete pInvestment;
};
template<typename... Ts>
std::unique_ptr<Investment, decltype(delInvmt1)>
makeInvestment(Ts&&... args);
is better than this:
void delInvmt2(Investment* pInvestment) {
makeLogEntry(pInvestment);
delete pInvestment;
}
template<typename... Ts>
std::unique_ptr<Investment, void (*)(Investment*)>
makeInvestment(Ts&&... params);
I can see that in the second case a pointer to the deleter function needs to be stored in the unique_ptr
, but why does nothing similar need to be stored for the lambda case?
Upvotes: 3
Views: 1170
Reputation: 3812
As @milleniumbug said, std::unique_ptr
use Empty Base Optimization
. It means you can declare a class with no data member:
class empty
{
public:
// methods
};
If you have another class that declare a member variable of empty
inside it, the size of your class will increase even if empty
has no data member:
class foo
{
public:
int i;
empty em;
};
In this case size of foo
will be 8 byte. But if you declare foo
to inherit from empty
, this inheritance has no effect in size of foo
and it's size will be 4 byte:
class foo : public empty
{
public:
int i;
};
If you take a look into std::unique_ptr
implementation of your compiler you will see this. I'm using VC++ 2015 and in this compiler structure of std::unique_ptr
is like below:
template<class _Ty, class _Dx> // = default_delete<_Ty>
class unique_ptr : public _Unique_ptr_base<_Ty, _Dx>
It inherit from this class:
template<class _Ty, class _Dx>
class _Unique_ptr_base
{ // stores pointer and deleter
public:
...
_Compressed_pair<_Dx, pointer> _Mypair;
};
In _Unique_ptr_base
there is a member of type _Compressed_pair
. This class is declared in this way:
template<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>
class _Compressed_pair final
: private _Ty1
{ // store a pair of values, deriving from empty first
private:
_Ty2 _Myval2;
Actually this class is specialized if it's second template argument is an empty class. In this case it inherit from empty deleter class and declare a member variable of first template argument that is std::unique_ptr
pointer.
Upvotes: 3