Fwiw, I think the design can be simplified, assuming a C++11 conforming container:
template <class T>
class no_init_allocator
{
public:
typedef T value_type;
no_init_allocator() noexcept {}
template <class U>
no_init_allocator(const no_init_allocator<U>&) noexcept {}
T* allocate(std::size_t n)
{return static_cast<T*>(::operator new(n * sizeof(T)));}
void deallocate(T* p, std::size_t) noexcept
{::operator delete(static_cast<void*>(p));}
template <class U>
void construct(U*) noexcept
{
static_assert(std::is_trivially_default_constructible<U>::value,
"This allocator can only be used with trivally default constructible types");
}
template <class U, class A0, class... Args>
void construct(U* up, A0&& a0, Args&&... args) noexcept
{
::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
}
};
I see little advantage to deriving from another allocator.
Now you can let allocator_traits
handle rebind
.
Template the construct
members on U
. This helps if you want to use this allocator with some container that needs to allocate something other than a T
(e.g. std::list
).
Move the static_assert
test into the single construct
member where it is important.
You can still create a using
:
template <class T>
using uninitialised_vector = std::vector<T, no_init_allocator<T>>;
And this still fails to compile:
unitialised_vector< std::vector<int> > x(10);
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think the test for is_trivially_destructible
is overkill, unless you also optimize destroy
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:
class A
{
int data_;
public:
A() = default;
A(int d) : data_(d) {}
};
int main()
{
uninitialised_vector<A> v(10);
}
And it just works. But if you make ~A()
non trivial:
~A() {std::cout << "~A(" << data_ << ")
";}
Then, at least on my system, you get an error on construction:
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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:
uninitialised_vector<A> v;
v.push_back(A());
This works, only because I didn't overreach with requiring a trivial destructor. And when executing this I get ~A()
to run as expected:
~A(0)
~A(0)