I recently had some interest for std::allocator, thinking it might solve an issue I had with some design decision on C++ code.
Now I've read some documentation about it, watched some videos, like Andrei Alexandrescu's one at CppCon 2015, and I now basically understand I shouldn't use them, because they're not designed to work the way I think allocators might work.
That being said, before realising this, I write some test code to see how a custom subclass of std::allocator could work.
Obviously, didn't work as expected... : )
So the question is not about how allocators should be used in C++, but I'm just curious to learn exactly why my test code (provided below) is not working.
Not because I want to use custom allocators. Just curious to see the exact reason...
typedef std::basic_string< char, std::char_traits< char >, TestAllocator< char > > TestString;
int main( void )
{
    TestString s1( "hello" );
    TestString s2( s1 );
    s1 += ", world";
    std::vector< int, TestAllocator< int > > v;
    v.push_back( 42 );
    return 0;
}
Complete code for TestAllocator is provided at the end of this question.
Here I'm simply using my custom allocator with some std::basic_string, and with std::vector.
With std::basic_string, I can see an instance of my allocator is actually created, but no method is called...
So it just looks like it's not used at all.
But with std::vector, my own allocate method is actually being called.
So why is there a difference here?
I did try with different compilers and C++ versions.
Looks like the old GCC versions, with C++98, do call allocate on my TestString type, but not the new ones with C++11 and later.
Clang also don't call allocate.
So just curious to see an explanation about these different behaviours.
Allocator code:
template< typename _T_ >
struct TestAllocator
{
    public:
        typedef       _T_   value_type;
        typedef       _T_ * pointer;
        typedef const _T_ * const_pointer;
        typedef       _T_ & reference;
        typedef const _T_ & const_reference;
        typedef std::size_t    size_type;
        typedef std::ptrdiff_t difference_type;
        typedef std::true_type propagate_on_container_move_assignment;
        typedef std::true_type is_always_equal;
        template< class _U_ >
        struct rebind
        {
            typedef TestAllocator< _U_ > other;
        };
        TestAllocator( void ) noexcept
        {
            std::cout << "CTOR" << std::endl;
        }
        TestAllocator( const TestAllocator & other ) noexcept
        {
            ( void )other;
            std::cout << "CCTOR" << std::endl;
        }
        template< class _U_ > 
        TestAllocator( const TestAllocator< _U_ > & other ) noexcept
        {
            ( void )other;
            std::cout << "CCTOR" << std::endl;
        }
        ~TestAllocator( void )
        {
            std::cout << "DTOR" << std::endl;
        }
        pointer address( reference x ) const noexcept
        {
            return std::addressof( x );
        }
        pointer allocate( size_type n, std::allocator< void >::const_pointer hint = 0 )
        {
            pointer p;
            ( void )hint;
            std::cout << "allocate" << std::endl;
            p = new _T_[ n ]();
            if( p == nullptr )
            {
                throw std::bad_alloc()  ;
            }
            return p;
        }
        void deallocate( _T_ * p, std::size_t n )
        {
            ( void )n;
            std::cout << "deallocate" << std::endl;
            delete[] p;
        }
        const_pointer address( const_reference x ) const noexcept
        {
            return std::addressof( x );
        }
        size_type max_size() const noexcept
        {
            return size_type( ~0 ) / sizeof( _T_ );
        }
        void construct( pointer p, const_reference val )
        {
            ( void )p;
            ( void )val;
            std::cout << "construct" << std::endl;
        }
        void destroy( pointer p )
        {
            ( void )p;
            std::cout << "destroy" << std::endl;
        }
};
template< class _T1_, class _T2_ >
bool operator ==( const TestAllocator< _T1_ > & lhs, const TestAllocator< _T2_ > & rhs ) noexcept
{
    ( void )lhs;
    ( void )rhs;
    return true;
}
template< class _T1_, class _T2_ >
bool operator !=( const TestAllocator< _T1_ > & lhs, const TestAllocator< _T2_ > & rhs ) noexcept
{
    ( void )lhs;
    ( void )rhs;
    return false;
}
 
     
    