Re: C++0x コンストラクタの引数をとる push_back

http://d.hatena.ne.jp/faith_and_brave/20080310/1205149207
なんでメモリリークするのかは考えてもらう(笑)として、検証用にアロケータを作成したので貼っておきます。

#include <boost/config.hpp>
#include <memory>

bool allocated = false;

template<class T>
class once_allocator;

template<>
class once_allocator<void>
{
public:
    typedef void* pointer;
    typedef const void* const_pointer;
    typedef void value_type;

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

template<class T>
class once_allocator
{
public:
    typedef std::size_t size_type;
    typedef std::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 once_allocator<U> other; };

    once_allocator() throw()
    {
    }

    once_allocator(const once_allocator&) throw()
    {
    }

    template <class U> once_allocator(const once_allocator<U>&) throw()
    {
    }

    ~once_allocator() throw()
    {
    }

    pointer address(reference x) const
    {
        return &x;
    }

    const_pointer address(const_reference x) const
    {
        return &x;
    }

    pointer allocate(
        size_type n, once_allocator<void>::const_pointer hint = 0)
    {
        if (allocated)
            throw std::bad_alloc();

        T* p = static_cast<T*>(::operator new(n * sizeof(T)));
        allocated = true;
        return p;
    }

    void deallocate(pointer p, size_type n)
    {
        ::operator delete(static_cast<void*>(p));
    }

    size_type max_size() const throw()
    {
        return 1;
    }

    void construct(pointer p, const T& val)
    {
        new(static_cast<void*>(p)) T(val);
    }

#if defined(BOOST_HAS_VARIADIC_TMPL) && defined(BOOST_HAS_RVALUE_REFS)
    template<class... Args> void construct(pointer p, Args&&... args)
    {
        new(static_cast<void*>(p)) T(args...);
    }
#endif

    void destroy(pointer p)
    {
        p->~T();
    }
};

template<typename T>
inline bool operator==(const once_allocator<T>&, const once_allocator<T>&)
{
    return true;
}

template<typename T>
inline bool operator!=(const once_allocator<T>&, const once_allocator<T>&)
{
    return false;
}

あとは、vectorの要素型のデストラクタで何か出力すれば確認できます。

using namespace std;

struct person {
    int age;
    string name;

    person(int age, const string& name)
        : age(age), name(name)
    {
        cout << static_cast<void*>(this)
             << " person::person(int,const string&)"
             << endl;
    }

    ~person()
    {
        cout << static_cast<void*>(this)
             << " person::~person()"
             << endl;
    }
};

int main()
{
    try
    {
        vector<shared_ptr<person>,once_allocator<shared_ptr<person>>> v;
        v.reserve(1);

        // before
        v.push_back(shared_ptr<person>(new person(43, "Kikuko")));

        // after
        v.push_back(new person(17, "Kikuko"));
    }
    catch (const std::exception& e)
    {
        cerr << "Error: " << e.what() << endl;
    }

    return 0;
}

実行結果

0x1170300 person::person(int,const string&)
0x1180348 person::person(int,const string&)
0x1170300 person::~person()
Error: vector::_M_insert_aux

ちなみに、上のテストコードは空ベクタのcapacity()が0かつ、reserve()で要求サイズきっかり確保する実装じゃないと動きません。あしからず。
#エラーメッセージ見る限り、bad_allocより前にmax_size()のチェックに引っかかっているっぽいですね。