Following from this question, I want to use an unitialised_allocator
with, say, std::vector
to avoid default initialisation of elements upon construction (or resize()
of the std::vector
(see also here for a use case). My current design looks like this:
// based on a design by Jared Hoberock
template<typename T, typename base_allocator >
struct uninitialised_allocator : base_allocator::template rebind<T>::other
{
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_default_constructible<T>::value,
"value type must be default constructible");
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_destructible<T>::value,
"value type must be default destructible");
using base_t = typename base_allocator::template rebind<T>::other;
template<typename U>
struct rebind
{
typedef uninitialised_allocator<U, base_allocator> other;
};
typename base_t::pointer allocate(typename base_t::size_type n)
{
return base_t::allocate(n);
}
// catch default construction
void construct(T*)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)default_
{
base_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};
Then an unitialised_vector<>
template could be defined like this:
template<typename T, typename base_allocator = std::allocator<T>>
using uninitialised_vector =
std::vector<T,uninitialised_allocator<T,base_allocator>>;
However, as indicated by my comments, I'm not 100% certain as to what are the appropriate conditions in the static_assert()
? (Btw, one may consider SFINAE instead -- any useful comments on this are welcome)
Obviously, one has to avoid the disaster that would ensue from the attempted non-trivial destruction of an uninitialised object. Consider
unitialised_vector< std::vector<int> > x(10); // dangerous.
It was suggested (comment by Evgeny Panasyuk) that I assert trivial constructibility, but this does not seem to catch the above disaster scenario. I just tried to check what clang says about std::is_trivially_default_constructible<std::vector<int>>
(or std::is_trivially_destructible<std::vector<int>>
) but all I got was a crash of clang 3.2 ...
Another, more advanced, option would be to design an allocator which only elides the default construction for objects for which this would be safe to do so.
Fwiw, I think the design can be simplified, assuming a C++11 conforming container:
I see little advantage to deriving from another allocator.
Now you can let
allocator_traits
handlerebind
.Template the
construct
members onU
. This helps if you want to use this allocator with some container that needs to allocate something other than aT
(e.g.std::list
).Move the
static_assert
test into the singleconstruct
member where it is important.You can still create a
using
:And this still fails to compile:
I think the test for
is_trivially_destructible
is overkill, unless you also optimizedestroy
to do nothing. But I see no motivation in doing that since I believe it should get optimized anyway whenever appropriate. Without such a restriction you can:And it just works. But if you make
~A()
non trivial:Then, at least on my system, you get an error on construction:
I.e.
A
is no longer trivially constructible if it has a non-trivial destructor.However even with the non-trivial destructor you can still:
This works, only because I didn't overreach with requiring a trivial destructor. And when executing this I get
~A()
to run as expected: