In 23.2.1p3 C++11 Standart we can read:
For the components affected by this subclause that declare an
allocator_type
, objects stored in these components shall be constructed using theallocator_traits<allocator_type>::construct
function and destroyed using theallocator_traits<allocator_type>::destroy
function (20.6.8.2). These functions are called only for the container’s element type, not for internal types used by the container. [ Note: This means, for example, that a node-based container might need to construct nodes containing aligned buffers and callconstruct
to place the element into the buffer. —end note ]
allocator_traits<allocator_type>::construct
just call passed allocator's construct
method, if allocator defines one. I tried to use this and create allocator, which use list-initialization for construction, so I can utilize emplace
for aggregate initialization:
#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <cmath>
template<typename T>
struct init_list_allocator : public std::allocator<T> {
template<typename... Args>
void construct(T* p, Args&&... args)
{ ::new((void *)p) T{std::forward<Args>(args)...}; }
// Fix copy-constructors usage for aggregates
void construct(T* p, T& copy_construct_arg)
{ std::allocator<T>::construct(p, copy_construct_arg); }
void construct(T* p, const T& copy_construct_arg)
{ std::allocator<T>::construct(p, copy_construct_arg); }
void construct(T* p, const T&& copy_construct_arg)
{ std::allocator<T>::construct(p, std::move(copy_construct_arg)); }
void construct(T *p, T&& move_construct_arg)
{ std::allocator<T>::construct(p, std::move(move_construct_arg)); }
};
template<class T>
using improved_vector = std::vector<T, init_list_allocator<T>>;
struct A {
int x;
double y;
const char* z;
};
int main()
{
using namespace std;
vector<string> strings;
improved_vector<A> v;
for (int i = 0; i < 21; ++i) {
strings.emplace_back(to_string(i*i));
v.emplace_back(i, sqrt(i), strings.back().c_str());
};
for (const auto& elem : v)
cout << elem.x << ' ' << elem.y << ' ' << elem.z << '\n';
}
However, at least in gcc and clang, this doesn't work. The problem is, that their implementations of vector
use Allocator::rebind<T>::other::construct
instead of Allocator::construct
. And, because of our inheritance from std::allocator
, this rebind
gives std::allocator<T>::construct
. Ok, no problem, just add
template<typename U>
struct rebind {
using other = init_list_allocator<U>;
};
in our allocator's definition and this code will work. Great, now let's change vector
to list
. Here we have the unsolvable problem, because instead of Allocator::construct
object is initialized inside std::_List_node<_Tp>
constuctor in direct-initialization form (form with brackets).
Are this 2 issues a standard violations or I miss something?