emplace_back でも uniform initialization したい


なんかモヤモヤしたのでアロケータを自作して対応してみました。
アロケータ本体の実装はここら辺のコードを丸パクリ。

[ソース]

#include <vector>
#include <string>
#include <iostream>
#include <limits>


template <class T>
class uniform_initialization_allocator;


template<>
class uniform_initialization_allocator<void>
{
public:
  typedef size_t      size_type;
  typedef ptrdiff_t   difference_type;
  typedef void*       pointer;
  typedef const void* const_pointer;
  typedef void        value_type;

  template<typename _Tp1>
    struct rebind
    { typedef uniform_initialization_allocator<_Tp1> other; };
};


template <class T>
struct uniform_initialization_allocator{
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    template <class U>
    struct rebind
    {
        typedef uniform_initialization_allocator<U> other;
    };

    uniform_initialization_allocator() noexcept{}
    uniform_initialization_allocator(const uniform_initialization_allocator&) noexcept{}

    template <class U>
    uniform_initialization_allocator(const uniform_initialization_allocator<U>&) noexcept{}
    // デストラクタ
    ~uniform_initialization_allocator() noexcept{}

    // メモリを割り当てる
    pointer
    allocate(size_type num, uniform_initialization_allocator<void>::const_pointer hint = 0){
        return (pointer)( ::operator new( num * sizeof(T) ) );
    }

    pointer address(reference value) const { return &value; }
    const_pointer address(const_reference value) const { return &value; }

    size_type max_size() const noexcept
    {
        return std::numeric_limits<size_t>::max() / sizeof(T);
    }

    void deallocate(pointer p, size_type num)
    {
        ::operator delete( (void*)p );
    }
    void destroy(pointer p)
    {
        p->~T();
    }

    // 割当て済みの領域を初期化する
    void construct(pointer p, const T& value)
    {
        new( (void*)p ) T(value);
    }
    void construct(pointer p, T& value)
    {
        new( (void*)p ) T(value);
    }
    void construct(pointer p, T&& value)
    {
        new( (void*)p ) T(std::move(value));
    }

    template<typename Up, typename... Args>
    void
    construct(Up* p, Args&&... args){
        ::new((void *)p) Up{ std::forward<Args>(args)... };
    }

};

template <class T1, class T2>
bool
operator ==(const uniform_initialization_allocator<T1>&, const uniform_initialization_allocator<T2>&){ return true; }

template <class T1, class T2>
bool
operator !=(const uniform_initialization_allocator<T1>&, const uniform_initialization_allocator<T2>&){ return false; }


struct X{
    int a;
    double b;
    std::string c;
};


int
main(){
    std::vector<X, ::uniform_initialization_allocator<X>> xs;

    xs.emplace_back(42, 3.14, "homu");

    std::cout << xs[0].a << std::endl;
    std::cout << xs[0].b << std::endl;
    std::cout << xs[0].c << std::endl;

    return 0;
}

[出力]

42
3.14
homu


長々と書いてありますが、実際に実装すべき部分は uniform initialization で初期化するようにした construct メンバ関数だけですね。
アロケータで対応できるのはいいですね。