[cfe-dev] __vector_base::__destruct_at_end
Howard Hinnant
hhinnant at apple.com
Wed Jun 26 07:59:57 PDT 2013
On Jun 26, 2013, at 10:31 AM, Shriramana Sharma <samjnaa at gmail.com> wrote:
> Hello. I've been examining the libc++ sources primarily to know about
> how containers are implemented, and I note this member function of
> __vector_base:
>
> void __destruct_at_end(const_pointer __new_last, true_type) _NOEXCEPT;
>
> Where is it used? In the same file I only observe calls to:
>
> __destruct_at_end(const_pointer __new_last)
>
> which delegates to:
>
> __destruct_at_end(const_pointer __new_last, false_type)
>
> Or have I missed something? Also, why the two functions?
This is an optimization that I found was not working, so I disabled it with the intent of pulling it out completely if the disabilization went well. It has, I'll pull it out.
>
> Also: why separate all the containers into __container and
> __container_base with the former inheriting from the latter? Can't it
> be done in a single class?
This is a C++03 technique for making container constructors exception safe. Let's look at an example:
template <class _Tp, class _Allocator>
template <class _InputIterator>
vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a)
: __base(__a)
{
for (; __first != __last; ++__first)
push_back(*__first);
}
I've removed the _InputIterator constraint, and the debugging support to focus the discussion. If any of the push_back's throw an exception, this constructor must undo all previous push_back's. This is the job of the base class. The base class is completely constructed prior to the start of any of the push_back's. So its destructor will run. However the derived class's destructor (~vector()) won't run until vector's constructor completes. I.e. without the base class running ~__vector_base() (which calls clear() and deallocate), this constructor would leak memory if an exception is thrown.
A C++03 alternative is to use try catch:
template <class _Tp, class _Allocator>
template <class _InputIterator>
vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a)
: allocator_(__a) // no base class, just store the allocator
{
try
{
for (; __first != __last; ++__first)
push_back(*__first);
}
catch (...)
{
clear();
shrink_to_fit();
throw;
}
}
In C++11 there is an even better way. However I don't use the following in libc++ because I want the library to work (more or less) in C++03 mode. But for completeness, here it is:
template <class _Tp, class _Allocator>
template <class _InputIterator>
vector<_Tp, _Allocator>::vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a)
: vector(__a)
{
for (; __first != __last; ++__first)
push_back(*__first);
}
This constructor forwards to the vector constructor that just takes an allocator. Once that constructor is complete (all it does is store the allocator), then ~vector() will run if there is an exception in the "outer" constructor.
Howard
More information about the cfe-dev
mailing list