// bslma_allocatortraits.h                                            -*-C++-*-
#ifndef INCLUDED_BSLMA_ALLOCATORTRAITS
#define INCLUDED_BSLMA_ALLOCATORTRAITS

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a uniform interface to standard allocator types.
//
//@CLASSES:
//  bsl::allocator_traits: Uniform interface to standard allocator types
//
//@SEE_ALSO: bslma_allocator, bslma_bslallocator
//
// TBD: update component-level doc
//@DESCRIPTION: The standard `allocator_traits` class template is defined in
// the C++11 standard ([allocator.traits]) as a uniform mechanism for accessing
// nested types within, and operations on, any standard-conforming allocator.
// An `allocator_traits` specialization is stateless, and all of its member
// functions are static.  In most cases, facilities of `allocator_traits` are
// straight pass-throughs for the same facilities from the `ALLOC` template
// parameter.  For example, `allocator_traits<X>::pointer` is the same as
// `X::pointer` and `allocator_traits<X>::allocate(x, n)` is the same as
// `x.allocate(n)`.  The advantage of using `allocator_traits` instead of
// directly using the allocator is that the `allocator_traits` interface can
// supply parts of the interface that are missing from `ALLOC`.  In fact, the
// most important purpose of `allocator_traits` is to provide implementations
// of C++11 allocator features that were absent in C++03, thus allowing a C++03
// allocator to work with C++11 containers.
//
// This component provides a full C++11 interface for `allocator_traits`, but
// constrains the set of allocator types on which it may be instantiated.
// Specifically, this implementation does not provide defaults for C++03 types
// and functions, and has hard-wired implementations of the new C++11 features.
// Thus, the `allocator_traits` template cannot be instantiated on an allocator
// type that does not provide a full compliment of types and functions required
// by the C++03 standard, and it will ignore any special C++11 features
// specified in `ALLOC`.  This limitation exists because Bloomberg does not
// need the full functionality of the C++11 model, but needs only to
// distinguish between C++03 allocators and allocators that implement the BSLMA
// allocator model (see {`bslma_bslallocator`}).  The full feature set of
// `allocator_traits` would require a lot of resources for implementation and
// (especially) testing.  Moreover, a full implementation would require
// metaprogramming that is too advanced for the feature set of the compilers
// currently in use at Bloomberg.  This interface is useful, however, as a way
// to future-proof containers against the eventual implementation of the full
// feature set, and to take advantage of the Bloomberg-specific features
// described below.
//
// There are two important (new) C++11 features provided by the
// `allocator_traits` interface: the `construct` function having a
// variable-length argument list (limited to 5 constructor arguments on
// compilers that don't support variadic templates) and the
// allocator-propagation traits.  The implementations of these features within
// this component are tuned to Bloomberg's needs.  The `construct` member
// function will automatically forward the allocator to the constructed object
// iff the `ALLOC` parameter is convertible from `bslma::Allocator*` and the
// object being constructed has the `bslma::UsesBslmaAllocator` type trait, as
// per standard Bloomberg practice.  The
// `select_on_container_copy_construction` static member will return a
// default-constructed allocator iff `ALLOC` is convertible from
// `bslma::Allocator *` because bslma allocators should not be copied when a
// container is copy-constructed; otherwise this function will return a copy of
// the allocator, as per C++03 container rules.  The other propagation traits
// all have a `false` value, so allocators are not propagated on assignment or
// swap.
//
// Note that use of this component will differ from a strict following of the
// C++03 standard, as the `construct` and `destroy` methods of the
// parameterized allocator type will not be called.  Rather, the target object
// will always be constructed at the address specified by the user, by calling
// the constructor in-place.  Similarly, the destructor will always be called
// directly, rather than using a parameterized allocator's `destroy` method.
// Otherwise, this implementation will fully support the C++03 model, including
// use of allocators returning "smart pointers" from `allocate`.
//
///Usage
///-----
// In this section we show intended usage of this component.
//
///Example 1: A Container Class
///- - - - - - - - - - - - - -
// This example demonstrates the intended use of `allocator_traits` to
// implement a standard-conforming container class.  First, we create a
// container class that holds a single object and which meets the requirements
// both of a standard container and of a Bloomberg container.  I.e., when
// instantiated with an allocator argument it uses the standard allocator
// model; otherwise it uses the `bslma` model.  We provide an alias,
// `AllocTraits`, to the specific `allocator_traits` instantiation to simplify
// the implementation of each method that must allocate memory, or create or
// destroy elements.
// ```
// #include <bslma_allocatortraits.h>
//
// using namespace BloombergLP;
//
// /// This class provides a container that always holds exactly one
// /// element, dynamically allocated using the specified allocator.
// template <class TYPE, class ALLOC = bsl::allocator<TYPE> >
// class MyContainer {
//
//     /// Alias for the `allocator_traits` instantiation to use for all
//     /// memory management requests.
//     typedef bsl::allocator_traits<ALLOC> AllocTraits;
//
//     // DATA
//     ALLOC  d_allocator;
//     TYPE  *d_value_p;
//
//   public:
//     typedef TYPE  value_type;
//     typedef ALLOC allocator_type;
//     // etc.
//
//     // CREATORS
//     explicit MyContainer(const ALLOC& a = ALLOC());
//     explicit MyContainer(const TYPE& v, const ALLOC& a = ALLOC());
//     MyContainer(const MyContainer& other);
//     MyContainer(const MyContainer& other, const ALLOC& a);
//     ~MyContainer();
//
//     // MANIPULATORS
//     ALLOC get_allocator() const { return d_allocator; }
//
//     // ACCESSORS
//     TYPE&       front()       { return *d_value_p; }
//     const TYPE& front() const { return *d_value_p; }
//
//     // etc.
// ```
// Next we define the type traits for `MyContainer` so that it is recognized as
// an STL *sequence* container:
// * Defines STL iterators
// * Is bitwise moveable if the allocator is bitwise moveable
// * Uses `bslma` allocators if the `ALLOC` template parameter is convertible
//   from `bslma::Allocator*`.
// ```
//     // TRAITS
//
//     // We would do the following if 'bslalg' was accessible.
//     // BSLMF_NESTED_TRAIT_DECLARATION(
//     //    MyContainer, bslalg::HasStlIterators);
//
//     BSLMF_NESTED_TRAIT_DECLARATION_IF(
//         MyContainer,
//         bslmf::IsBitwiseMoveable,
//         bslmf::IsBitwiseMoveable<ALLOC>::value);
//
//     BSLMF_NESTED_TRAIT_DECLARATION_IF(
//         MyContainer,
//         bslma::UsesBslmaAllocator,
//         (bsl::is_convertible<bslma::Allocator*, ALLOC>::value));
// };
// ```
// Then we implement the constructors, which allocate memory and construct a
// `TYPE` object in the allocated memory.  Because the allocation and
// construction are done in two separate steps, we need to create a proctor
// that will deallocate the allocated memory in case the constructor throws an
// exception.  The proctor uses the uniform interface provided by
// `allocator_traits` to access the `pointer` and `deallocate` members of
// `ALLOC`:
// ```
// /// This class implements a proctor to release memory allocated during
// /// the construction of a `MyContainer` object if the constructor for
// /// the container's data element throws an exception.  Such a proctor
// /// should be `release`d once the element is safely constructed.
// template <class ALLOC>
// class MyContainerProctor {
//
//     typedef typename bsl::allocator_traits<ALLOC>::pointer pointer;
//     ALLOC   d_alloc;
//     pointer d_data_p;
//
//   public:
//     MyContainerProctor(const ALLOC& a, pointer p)
//         : d_alloc(a), d_data_p(p) { }
//
//     ~MyContainerProctor() {
//         if (d_data_p) {
//             bsl::allocator_traits<ALLOC>::deallocate(d_alloc, d_data_p, 1);
//         }
//     }
//
//     void release() { d_data_p = pointer(); }
// };
// ```
// Next, we perform the actual allocation and construction using the `allocate`
// and `construct` members of `allocator_traits`, which provide the correct
// semantic for passing the allocator to the constructed object when
// appropriate:
// ```
// template <class TYPE, class ALLOC>
// MyContainer<TYPE, ALLOC>::MyContainer(const ALLOC& a)
//     : d_allocator(a)
// {
//     d_value_p = AllocTraits::allocate(d_allocator, 1);
//     MyContainerProctor<ALLOC> proctor(a, d_value_p);
//     // Call 'construct' with no constructor arguments
//     AllocTraits::construct(d_allocator, d_value_p);
//     proctor.release();
// }
//
// template <class TYPE, class ALLOC>
// MyContainer<TYPE, ALLOC>::MyContainer(const TYPE& v, const ALLOC& a)
//     : d_allocator(a)
// {
//     d_value_p = AllocTraits::allocate(d_allocator, 1);
//     MyContainerProctor<ALLOC> proctor(a, d_value_p);
//     // Call 'construct' with one constructor argument of type 'TYPE'
//     AllocTraits::construct(d_allocator, d_value_p, v);
//     proctor.release();
// }
// ```
// Next, the copy constructor for `MyContainer` needs to conditionally copy the
// allocator from the `other` container.  The copy constructor uses
// `allocator_traits::select_on_container_copy_construction` to decide whether
// to copy the `other` allocator (for non-bslma allocators) or to
// default-construct the allocator (for bslma allocators).
// ```
// template <class TYPE, class ALLOC>
// MyContainer<TYPE, ALLOC>::MyContainer(const MyContainer& other)
//     : d_allocator(bsl::allocator_traits<ALLOC>::
//                   select_on_container_copy_construction(other.d_allocator))
// {
//     d_value_p = AllocTraits::allocate(d_allocator, 1);
//     MyContainerProctor<ALLOC> proctor(d_allocator, d_value_p);
//     AllocTraits::construct(d_allocator, d_value_p, *other.d_value_p);
//     proctor.release();
// }
// ```
// Now, the destructor uses `allocator_traits` functions to destroy and
// deallocate the value object:
// ```
// template <class TYPE, class ALLOC>
// MyContainer<TYPE, ALLOC>::~MyContainer()
// {
//     AllocTraits::destroy(d_allocator, d_value_p);
//     AllocTraits::deallocate(d_allocator, d_value_p, 1);
// }
// ```
// Finally, we perform a simple test of `MyContainer`, instantiating it with
// element type `int`:
// ```
// int usageExample1()
// {
//     bslma::TestAllocator testAlloc;
//     MyContainer<int> C1(123, &testAlloc);
//     assert(C1.get_allocator() == bsl::allocator<int>(&testAlloc));
//     assert(C1.front() == 123);
//
//     MyContainer<int> C2(C1);
//     assert(C2.get_allocator() == bsl::allocator<int>());
//     assert(C2.front() == 123);
//
//     return 0;
// }
// ```
///Example 2: C++03 Allocators
///- - - - - - - - - - - - - -
// This example shows that when `MyContainer` is instantiated with a C++03
// allocator, that the allocator is a) copied on copy construction and b) is
// not propagated from the container to its elements.  Firstly we create a
// representative element class, `MyType`, that allocates memory using the
// bslma allocator protocol:
// ```
// #include <bslma_default.h>
//
// class MyType {
//
//     bslma::Allocator *d_allocator_p;
//     // etc.
//   public:
//     // TRAITS
//     BSLMF_NESTED_TRAIT_DECLARATION(MyType, bslma::UsesBslmaAllocator);
//
//     // CREATORS
//     explicit MyType(bslma::Allocator* basicAlloc = 0)
//        : d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
//     MyType(const MyType&)
//         : d_allocator_p(bslma::Default::allocator(0)) { /* ... */ }
//     MyType(const MyType&, bslma::Allocator* basicAlloc)
//        : d_allocator_p(bslma::Default::allocator(basicAlloc)) { /* ... */ }
//     // etc.
//
//     // ACCESSORS
//     bslma::Allocator *allocator() const { return d_allocator_p; }
//
//     // etc.
// };
// ```
// Then we create a C++03-style allocator class template:
// ```
// template <class TYPE>
// class MyCpp03Allocator {
//     int d_state;
//
//   public:
//     typedef TYPE        value_type;
//     typedef TYPE       *pointer;
//     typedef const TYPE *const_pointer;
//     typedef unsigned    size_type;
//     typedef int         difference_type;
//
//     template <class OTHER>
//     struct rebind {
//         typedef MyCpp03Allocator<OTHER> other;
//     };
//
//     // CREATORS
//     explicit MyCpp03Allocator(int state = 0) : d_state(state) { }
//
//     // ALLOCATION FUNCTIONS
//     TYPE* allocate(size_type n, const void* = 0)
//         { return static_cast<TYPE *>(::operator new(sizeof(TYPE) * n)); }
//
//     void deallocate(TYPE* p, size_type) { ::operator delete(p); }
//
//     // ELEMENT CREATION FUNCTIONS
//     template <class ELEMENT_TYPE>
//     void construct(ELEMENT_TYPE *p)
//     {
//         ::new (static_cast<void *>(p)) ELEMENT_TYPE();
//     }
//     template <class ELEMENT_TYPE, class A1>
//     void construct(ELEMENT_TYPE *p,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A1) a1)
//     {
//         ::new (static_cast<void *>(p))
//             ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1));
//     }
//     template <class ELEMENT_TYPE, class A1, class A2>
//     void construct(ELEMENT_TYPE *p,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A1) a1,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A2) a2)
//     {
//         ::new (static_cast<void *>(p))
//             ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
//                          BSLS_COMPILERFEATURES_FORWARD(A2, a2));
//     }
//
//     template <class ELEMENT_TYPE, class A1, class A2, class A3>
//     void construct(ELEMENT_TYPE *p,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A1) a1,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A2) a2,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A3) a3)
//     {
//         ::new (static_cast<void *>(p))
//             ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
//                          BSLS_COMPILERFEATURES_FORWARD(A2, a2),
//                          BSLS_COMPILERFEATURES_FORWARD(A3, a3));
//     }
//
//     template <class ELEMENT_TYPE, class A1, class A2, class A3, class A4>
//     void construct(ELEMENT_TYPE *p,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A1) a1,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A2) a2,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A3) a3,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A4) a4)
//     {
//         ::new (static_cast<void *>(p))
//             ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
//                          BSLS_COMPILERFEATURES_FORWARD(A2, a2),
//                          BSLS_COMPILERFEATURES_FORWARD(A3, a3),
//                          BSLS_COMPILERFEATURES_FORWARD(A4, a4));
//     }
//
//     template <class ELEMENT_TYPE,
//               class A1,
//               class A2,
//               class A3,
//               class A4,
//               class A5>
//     void construct(ELEMENT_TYPE *p,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A1) a1,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A2) a2,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A3) a3,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A4) a4,
//                    BSLS_COMPILERFEATURES_FORWARD_REF(A5) a5)
//     {
//         ::new (static_cast<void *>(p))
//             ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(A1, a1),
//                          BSLS_COMPILERFEATURES_FORWARD(A2, a2),
//                          BSLS_COMPILERFEATURES_FORWARD(A3, a3),
//                          BSLS_COMPILERFEATURES_FORWARD(A4, a4),
//                          BSLS_COMPILERFEATURES_FORWARD(A5, a5));
//     }
//
//     template <class ELEMENT_TYPE>
//     void destroy(ELEMENT_TYPE *p) { p->~ELEMENT_TYPE(); }
//
//     // ACCESSORS
//     static size_type max_size() { return UINT_MAX / sizeof(TYPE); }
//
//     int state() const { return d_state; }
// };
//
// template <class TYPE1, class TYPE2>
// inline
// bool operator==(const MyCpp03Allocator<TYPE1>& lhs,
//                 const MyCpp03Allocator<TYPE2>& rhs)
// {
//     return lhs.state() == rhs.state();
// }
//
// template <class TYPE1, class TYPE2>
// inline
// bool operator!=(const MyCpp03Allocator<TYPE1>& lhs,
//                 const MyCpp03Allocator<TYPE2>& rhs)
// {
//     return ! (lhs == rhs);
// }
// ```
// Finally we instantiate `MyContainer` using this allocator type and verify
// that elements are constructed using the default allocator (because the
// allocator is not propagated from the container).  We also verify that the
// allocator is copied on copy-construction:
// ```
// int usageExample2()
// {
//     typedef MyCpp03Allocator<MyType> MyTypeAlloc;
//
//     MyContainer<MyType, MyTypeAlloc> C1a(MyTypeAlloc(1));
//     assert((bsl::is_same<MyContainer<MyType, MyTypeAlloc>::allocator_type,
//                             MyTypeAlloc>::value));
//     assert(C1a.get_allocator() == MyTypeAlloc(1));
//     assert(C1a.front().allocator() == bslma::Default::defaultAllocator());
//
//     MyContainer<MyType, MyTypeAlloc> C2a(C1a);
//     assert(C2a.get_allocator() == C1a.get_allocator());
//     assert(C2a.get_allocator() != MyTypeAlloc());
//     assert(C2a.front().allocator() == bslma::Default::defaultAllocator());
//
//     MyType                           dummy;
//     MyContainer<MyType, MyTypeAlloc> C1b(dummy, MyTypeAlloc(1));
//     assert((bsl::is_same<MyContainer<MyType, MyTypeAlloc>::allocator_type,
//                             MyTypeAlloc>::value));
//     assert(C1b.get_allocator() == MyTypeAlloc(1));
//     assert(C1b.front().allocator() == bslma::Default::defaultAllocator());
//
//     MyContainer<MyType, MyTypeAlloc> C2b(C1b);
//     assert(C2b.get_allocator() == C1b.get_allocator());
//     assert(C2b.get_allocator() != MyTypeAlloc());
//     assert(C2b.front().allocator() == bslma::Default::defaultAllocator());
//
//     return 0;
// }
// ```

#include <bslscm_version.h>

#include <bslmf_enableif.h>
#include <bslmf_integralconstant.h>
#include <bslmf_isempty.h>
#include <bslmf_issame.h>
#include <bslmf_util.h>
#include <bslmf_voidtype.h>

#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>
#include <bsls_util.h>     // 'forward<T>(V)'

#if BSLS_COMPILERFEATURES_CPLUSPLUS >= 201703L
# include <memory>  // for `std::allocator_traits`
#endif

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// clang-format off
// Include version that can be compiled with C++03
// Generated on Mon Jan 13 08:31:27 2025
// Command line: sim_cpp11_features.pl bslma_allocatortraits.h

# define COMPILING_BSLMA_ALLOCATORTRAITS_H
# include <bslma_allocatortraits_cpp03.h>
# undef COMPILING_BSLMA_ALLOCATORTRAITS_H

// clang-format on
#else

#include <limits>

namespace BloombergLP {
namespace bslma {


                      // ================================
                      // AllocatorTraits_HasIsAlwaysEqual
                      // ================================

/// This `struct` template provides a mechanism for determining whether a
/// given (template parameter) `ALLOCATOR_TYPE` defines a nested alias named
///`is_always_equal`.  The static boolean member `value` (nested alias
/// named `type`) is `true` (`bsl::true_type`) if `ALLOCATOR_TYPE` defines
/// such an alias, and `false` (`bsl::false_type`) otherwise.
template <class ALLOC>
struct AllocatorTraits_HasIsAlwaysEqual {

  private:
    // PRIVATE TYPES
    typedef struct { char d_a;    } yes_type;
    typedef struct { char d_a[2]; } no_type;

    // PRIVATE CLASS METHODS

    /// Return `yes_type` if the (template parameter) `TYPE` defines a
    /// nested alias named `is_always_equal`, and `no_type` otherwise.
    template <class U>
    static yes_type match(typename U::is_always_equal *);
    template <class U>
    static no_type match(...);

  public:
    // PUBLIC CLASS DATA
    static const bool value = sizeof(match<ALLOC>(0)) == sizeof(yes_type);

    // PUBLIC TYPES
    typedef bsl::integral_constant<bool, value> type;
};

                       // =============================
                       // AllocatorTraits_IsAlwaysEqual
                       // =============================

/// This `struct` template sets the boolean type for the attribute named
/// `is_always_equal` to the nested type alias in the given (template
/// parameter) `ALLOC` if `ALLOC` defines such an alias (i.e., if
/// `true == AllocatorTraits_HasIsAlwaysEqual<ALLOCATOR_TYPE>::value`).
template <class ALLOC, bool = AllocatorTraits_HasIsAlwaysEqual<ALLOC>::value>
struct AllocatorTraits_IsAlwaysEqual : public ALLOC::is_always_equal
{
};

/// This `struct` template sets the boolean type for the attribute named
/// `is_always_equal` to `bsl::is_empty<ALLOC>` if the given (template
/// parameter) `ALLOC` does not define such an alias (i.e., if
/// `false == AllocatorTraits_HasIsAlwaysEqual<ALLOCATOR_TYPE>::value`).
template <class ALLOC>
struct AllocatorTraits_IsAlwaysEqual<ALLOC, false>
                                  : public bsl::is_empty<ALLOC>
{
};

                   // =====================================
                   // AllocatorTraits_HasSelectOnCopyMethod
                   // =====================================

/// This `struct` template provides a mechanism for determining whether a
/// given (template parameter) `ALLOCATOR_TYPE` defines a `const` member
/// function named `select_on_container_copy_construction` that takes no
/// arguments and returns an `ALLOCATOR_TYPE` object by value.  The static
/// boolean `value` (nested `type` alias) is `true` (`bsl::true_type`) if
/// `ALLOCATOR_TYPE` defines such a method, and `false` (`bsl::false_type`)
/// otherwise.
template <class ALLOCATOR_TYPE>
struct AllocatorTraits_HasSelectOnCopyMethod {

  private:
    typedef struct { char a;    } yes_type;
    typedef struct { char a[2]; } no_type;

    /// This `struct` template provides a mechanism to check if a type
    /// matches an instance within a SFINAE context.
    template <class T, T> struct MatchType { };

    template <class T>
    struct MethodAlias { typedef T (T::*Method)() const; };

    /// Return `yes_type` if the (template parameter) `TYPE` defines a const
    /// member function named `select_on_container_copy_construction` taking
    /// no arguments and returning a `TYPE` object by value, and `no_type`
    /// otherwise.
    template <class TYPE>
    static yes_type match(MatchType<typename MethodAlias<TYPE>::Method,
                          &TYPE::select_on_container_copy_construction> *);
    template <class TYPE>
    static no_type match(...);

  public:
    static const bool value =
                          sizeof(match<ALLOCATOR_TYPE>(0)) == sizeof(yes_type);
    typedef bsl::integral_constant<bool, value> type;
};

                    // ===================================
                    // AllocatorTraits_HasPropOnCopyAssign
                    // ===================================

/// This `struct` template provides a mechanism for determining whether a
/// given (template parameter) `ALLOCATOR_TYPE` defines a nested alias named
///`propagate_on_container_copy_assignment`.  The static boolean member
/// `value` (nested alias named `type`) is `true` (`bsl::true_type`) if
/// `ALLOCATOR_TYPE` defines such an alias, and `false` (`bsl::false_type`)
/// otherwise.
template <class ALLOCATOR_TYPE>
struct AllocatorTraits_HasPropOnCopyAssign {

  private:
    typedef struct { char a;    } yes_type;
    typedef struct { char a[2]; } no_type;

    /// Return `yes_type` if the (template parameter) `TYPE` defines a
    /// nested alias named `propagate_on_container_copy_assignment`, and
    /// `no_type` otherwise.
    template <class U>
    static
    yes_type match(typename U::propagate_on_container_copy_assignment *);
    template <class U>
    static no_type match(...);

  public:
    static const bool value =
                          sizeof(match<ALLOCATOR_TYPE>(0)) == sizeof(yes_type);
    typedef bsl::integral_constant<bool, value> type;
};

                      // ================================
                      // AllocatorTraits_PropOnCopyAssign
                      // ================================

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_copy_assignment` to `bsl::false_type` if the
/// given (template parameter) `ALLOCATOR_TYPE` does not define such an
/// alias (i.e.,
/// `false == AllocatorTraits_HasPropOnCopyAssign<ALLOCATOR_TYPE>::value`).
template <class ALLOCATOR_TYPE,
          bool = AllocatorTraits_HasPropOnCopyAssign<ALLOCATOR_TYPE>::value>
struct AllocatorTraits_PropOnCopyAssign : bsl::false_type
{
};

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_copy_assignment` to the nested type alias in the
/// given (template parameter) `ALLOCATOR_TYPE` if `ALLOCATOR_TYPE` defines
/// such an alias (i.e.,
/// `true == AllocatorTraits_HasPropOnCopyAssign<ALLOCATOR_TYPE>::value`).
template <class ALLOC>
struct AllocatorTraits_PropOnCopyAssign<ALLOC, true>
                       : public ALLOC::propagate_on_container_copy_assignment
{
};

                    // ===================================
                    // AllocatorTraits_HasPropOnMoveAssign
                    // ===================================

/// This `struct` template provides a mechanism for determining whether a
/// given (template parameter) `ALLOCATOR_TYPE` defines a nested alias named
///`propagate_on_container_move_assignment`.  The static boolean member
/// `value` (nested alias named `type`) is `true` (`bsl::true_type`) if
/// `ALLOCATOR_TYPE` defines such an alias, and `false` (`bsl::false_type`)
/// otherwise.
template <class ALLOC>
struct AllocatorTraits_HasPropOnMoveAssign {

  private:
    typedef struct { char a;    } yes_type;
    typedef struct { char a[2]; } no_type;

    /// Return `yes_type` if the (template parameter) `TYPE` defines a
    /// nested alias named `propagate_on_container_move_assignment`, and
    /// `no_type` otherwise.
    template <class U>
    static
    yes_type match(typename U::propagate_on_container_move_assignment *);
    template <class U>
    static no_type match(...);

  public:
    static const bool value = sizeof(match<ALLOC>(0)) == sizeof(yes_type);
    typedef bsl::integral_constant<bool, value> type;
};

                      // ================================
                      // AllocatorTraits_PropOnMoveAssign
                      // ================================

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_move_assignment` to `bsl::false_type` if the
/// given (template parameter) `ALLOCATOR_TYPE` does not define such an
/// alias (i.e.,
/// `false == AllocatorTraits_HasPropOnMoveAssign<ALLOCATOR_TYPE>::value`).
template <class ALLOC,
          bool = AllocatorTraits_HasPropOnMoveAssign<ALLOC>::value>
struct AllocatorTraits_PropOnMoveAssign : bsl::false_type
{
};

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_move_assignment` to the nested type alias in the
/// given (template parameter) `ALLOCATOR_TYPE` if `ALLOCATOR_TYPE` defines
/// such an alias (i.e.,
/// `true == AllocatorTraits_HasPropOnMoveAssign<ALLOCATOR_TYPE>::value`).
template <class ALLOC>
struct AllocatorTraits_PropOnMoveAssign<ALLOC, true>
                       : public ALLOC::propagate_on_container_move_assignment
{
};

                       // =============================
                       // AllocatorTraits_HasPropOnSwap
                       // =============================

/// This `struct` template provides a mechanism for determining whether a
/// given (template parameter) `ALLOCATOR_TYPE` defines a nested alias named
///`propagate_on_container_swap`.  The static boolean member `value` (nested
/// alias named `type`) is `true` (`bsl::true_type`) if `ALLOCATOR_TYPE`
/// defines such an alias, and `false` (`bsl::false_type`) otherwise.
template <class ALLOC>
struct AllocatorTraits_HasPropOnSwap {

  private:
    typedef struct { char a;    } yes_type;
    typedef struct { char a[2]; } no_type;

    /// Return `yes_type` if the (template parameter) `TYPE` defines a
    /// nested alias named `propagate_on_container_swap`, and `no_type`
    /// otherwise.
    template <class U>
    static
    yes_type match(typename U::propagate_on_container_swap *);
    template <class U>
    static no_type match(...);

  public:
    static const bool value = sizeof(match<ALLOC>(0)) == sizeof(yes_type);
    typedef bsl::integral_constant<bool, value> type;
};

                         // ==========================
                         // AllocatorTraits_PropOnSwap
                         // ==========================

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_swap` to `bsl::false_type` if the given
/// (template parameter) `ALLOCATOR_TYPE` does not define such an alias
/// (i.e., `false == AllocatorTraits_HasPropOnSwap<ALLOCATOR_TYPE>::value`).
template <class ALLOC, bool = AllocatorTraits_HasPropOnSwap<ALLOC>::value>
struct AllocatorTraits_PropOnSwap : bsl::false_type
{
};

/// This `struct` template sets the boolean type for the attribute named
/// `propagate_on_container_swap` to the nested type alias in the given
/// (template parameter) `ALLOCATOR_TYPE` if `ALLOCATOR_TYPE` defines such
/// an alias (i.e.,
/// `false == AllocatorTraits_HasPropOnSwap<ALLOCATOR_TYPE>::value`).
template <class ALLOC>
struct AllocatorTraits_PropOnSwap<ALLOC, true>
                                  : public ALLOC::propagate_on_container_swap
{
};

#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE) &&                        \
    defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) &&               \
    defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)

                     // ==================================
                     // AllocatorTraits_HasConstructMethod
                     // ==================================

template <class T, class Return, class... Args>
struct AllocatorTraits_HasConstructMethod {
  private:
    template <class U>
    static auto match(U *) ->
        typename bsl::is_same<decltype(bslmf::Util::declval<U>().construct(
                                             bslmf::Util::declval<Args>()...)),
                              Return>::type;
    template <class>
    static bsl::false_type match(...);

  public:
    typedef decltype(match<T>(0)) type;
    static const bool value = type::value;
};

                      // ================================
                      // AllocatorTraits_HasDestroyMethod
                      // ================================

template <class T, class Return, class... Args>
struct AllocatorTraits_HasDestroyMethod {
  private:
    template <class U>
    static auto match(U *) ->
        typename bsl::is_same<decltype(bslmf::Util::declval<U>().destroy(
                                             bslmf::Util::declval<Args>()...)),
                              Return>::type;
    template <class>
    static bsl::false_type match(...);

  public:
    typedef decltype(match<T>(0)) type;
    static const bool value = type::value;
};

#endif

                        // ===========================
                        // AllocatorTraits_PointerType
                        // ===========================

template <class T, class = void>
struct AllocatorTraits_PointerType {
    typedef typename T::value_type *type;
};


template <class T>
struct AllocatorTraits_PointerType<T, BSLMF_VOIDTYPE(typename T::pointer)> {
    typedef typename T::pointer type;
};

                      // ================================
                      // AllocatorTraits_ConstPointerType
                      // ================================

template <class T, class = void>
struct AllocatorTraits_ConstPointerType {
    /// should be pointer_traits::rebind of template above
    typedef const typename T::value_type *type;
};


template <class T>
struct AllocatorTraits_ConstPointerType<
                                   T,
                                   BSLMF_VOIDTYPE(typename T::const_pointer)> {
    typedef typename T::const_pointer type;
};


                      // ===============================
                      // AllocatorTraits_VoidPointerType
                      // ===============================

template <class T, class = void>
struct AllocatorTraits_VoidPointerType {
    /// should be pointer_traits::rebind of template above
    typedef void *type;
};


template <class T>
struct AllocatorTraits_VoidPointerType<
                                   T,
                                   BSLMF_VOIDTYPE(typename T::void_pointer)> {
    typedef typename T::void_pointer type;
};

                    // ====================================
                    // AllocatorTraits_ConstVoidPointerType
                    // ====================================

template <class T, class = void>
struct AllocatorTraits_ConstVoidPointerType {
    /// should be pointer_traits::rebind of template above
    typedef const void *type;
};


template <class T>
struct AllocatorTraits_ConstVoidPointerType<
                             T,
                             BSLMF_VOIDTYPE(typename T::const_void_pointer)> {
    typedef typename T::const_void_pointer type;
};

                          // ========================
                          // AllocatorTraits_SizeType
                          // ========================

template <class T, class = void>
struct AllocatorTraits_SizeType {
    typedef std::size_t type;
};


template <class T>
struct AllocatorTraits_SizeType<T, BSLMF_VOIDTYPE(typename T::size_type)> {
    typedef typename T::size_type type;
};

                       // ==============================
                       // AllocatorTraits_DifferenceType
                       // ==============================

/// should be pointer_traits::rebind of template above
template <class T, class = void>
struct AllocatorTraits_DifferenceType {
    typedef std::ptrdiff_t type;

};


template <class T>
struct AllocatorTraits_DifferenceType<
                             T,
                             BSLMF_VOIDTYPE(typename T::difference_type)> {
    typedef typename T::difference_type type;
};

                        // ===========================
                        // AllocatorTraits_RebindFront
                        // ===========================

#if defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)
/// There shall be no member named `type` unless T is a class template with
/// only type parameters.
template <class T, class U>
struct AllocatorTraits_RebindFront {
};

template <template <class, class...> class ALLOC,
          class T,
          class ...ARGS,
          class U>
struct AllocatorTraits_RebindFront<ALLOC<T, ARGS...>, U> {
    using type = ALLOC<U, ARGS...>;
};
#else
template <class T, class U>
struct AllocatorTraits_RebindFront {
    // There shall be no member named 'type' unless T is a class template with
    // only type parameters.
};

template <template <class> class ALLOC,
          class T,
          class U>
struct AllocatorTraits_RebindFront<ALLOC<T>, U> {
    typedef ALLOC<U> type;
};
#endif

                        // ===========================
                        // AllocatorTraits_RebindAlloc
                        // ===========================

/// should be pointer_traits::rebind of template above
template <class T, class U, class = void>
struct AllocatorTraits_RebindAlloc {
    typedef typename AllocatorTraits_RebindFront<T, U>::type type;
};

template <class T, class U>
struct AllocatorTraits_RebindAlloc<
                      T,
                      U,
                      BSLMF_VOIDTYPE(typename T::template rebind<U>::other)> {
    typedef typename T::template rebind<U>::other type;
};

                        // ===========================
                        // AllocatorTraits_CallMaxSize
                        // ===========================

#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE)
template <class T, class = void>
struct AllocatorTraits_CallMaxSize {

    // PUBLIC TYPES
    typedef typename AllocatorTraits_SizeType<T>::type SizeType;

    // PUBLIC CLASS METHODS

    /// Return the maximum size of the specified (template) parameter `T`.
    /// Also note that this method is defined inline to work around a
    /// Windows compiler bug with SFINAE functions.
    static SizeType max_size(const T &)
    {
        return std::numeric_limits<SizeType>::max() /
               sizeof(typename T::value_type);
    }
};

// Due to the dependence on expression SFINAE to detect the presence of a
// 'max_size' member of the allocator, this is only done on more modern
// platforms.
template <class T>
struct AllocatorTraits_CallMaxSize<
    T,
    BSLMF_VOIDTYPE(decltype(bslmf::Util::declval<T>().max_size()))> {

    // PUBLIC TYPES
    typedef typename AllocatorTraits_SizeType<T>::type SizeType;

    // PUBLIC CLASS METHODS

    /// Return the maximum size of the specified `alloc`.  Also note that
    /// this method is defined inline to work around a Windows compiler bug
    /// with SFINAE functions.
    static SizeType max_size(const T &alloc)
    {
        return alloc.max_size();
    }
};
#endif

} // close namespace bslma
} // close enterprise namespace

namespace bsl {

                           // ======================
                           // class allocator_traits
                           // ======================

/// This class supports the complete interface of the C++11
/// `allocator_traits` class template, which provides a uniform mechanism
/// for accessing nested types within, and operations on, any
/// standard-conforming allocator.  A specialization of this class template
/// for `bsl::allocator` provides support for Bloomberg's `bslma` allocator
/// model (see the `bslma_bslallocator` component for more details).  In
/// C++11 compilation environments, the `construct` methods forward to the
/// allocator's `construct` method if such a method matching the (variable
/// number of) specified constructor arguments exists; otherwise, the
/// `construct` method falls back to invoking the constructor of the element
/// type directly.  In C++03 compilation environments, there is no reliable
/// way to detect if the type provide a method that matches a (variable
/// number of) specified arguments; therefore, we require that standard
/// allocator types define `construct` methods taking a variable number of
/// arguments in those environments.  This implementation is not
/// fully-standard-conforming in that it does not support deduce data types
/// that are not specified in the allocator.
template <class ALLOCATOR_TYPE>
struct allocator_traits {

  private:

    typedef typename BloombergLP::bslma::AllocatorTraits_HasSelectOnCopyMethod<
                                    ALLOCATOR_TYPE>::type DelegateSelectMethod;

    /// Return the result of invoking the
    /// `select_on_container_copy_construction` method on the specified
    /// `stdAllocator`.
    static
    ALLOCATOR_TYPE selectOnCopyConstruct(const ALLOCATOR_TYPE& stdAllocator,
                                         true_type);

    /// Return the specified `stdAllocator`.  Note that this behavior
    /// enforces a default policy of propagating the allocator on copy
    /// construction when using a standard allocator.
    static
    ALLOCATOR_TYPE selectOnCopyConstruct(const ALLOCATOR_TYPE& stdAllocator,
                                         false_type);

#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE) &&                        \
    defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) &&               \
    defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)
    /// Construct an object of (template parameter) type `ELEMENT_TYPE` at
    /// the specified `elementAddr`, either by 1) calling the `construct`
    /// method on `basicAllocator` with `elemAddr` and the specified
    /// (variable number of) `arguments` if the (template parameter) type
    /// `ALLOCATOR_TYPE` defines such a method, or 2) forwarding the
    /// specified (variable number of) `arguments` to the constructor of
    /// `ELEMENT_TYPE` directly (and ignoring `basicAllocator`) otherwise.
    /// The behavior is undefined unless `elementAddr` refers to valid,
    /// uninitialized storage.
    template <class ELEMENT_TYPE, class... Args>
    static typename bsl::enable_if<
        BloombergLP::bslma::AllocatorTraits_HasConstructMethod<ALLOCATOR_TYPE,
                                                               void,
                                                               ELEMENT_TYPE *,
                                                               Args...>::value,
        void>::type
    privateConstruct(ALLOCATOR_TYPE&  basicAllocator,
                     ELEMENT_TYPE    *elementAddr,
                     Args&&...        arguments);
    template <class ELEMENT_TYPE, class... Args>
    static typename bsl::enable_if<
        !BloombergLP::bslma::AllocatorTraits_HasConstructMethod<
            ALLOCATOR_TYPE,
            void,
            ELEMENT_TYPE *,
            Args...>::value,
        void>::type
    privateConstruct(ALLOCATOR_TYPE&  basicAllocator,
                     ELEMENT_TYPE    *elementAddr,
                     Args&&...        arguments);

    template <class ELEMENT_TYPE>
    static typename bsl::enable_if<
        BloombergLP::bslma::AllocatorTraits_HasDestroyMethod<
            ALLOCATOR_TYPE,
            void,
            ELEMENT_TYPE *>::value,
        void>::type
    privateDestroy(ALLOCATOR_TYPE& basicAllocator, ELEMENT_TYPE *elementAddr);

    /// Destroy the object of (template parameter) type `ELEMENT_TYPE` at
    /// the specified `elementAddr`, either by 1) calling the `destroy`
    /// method on `basicAllocator` with `elemAddr` as the sole argument if
    /// the (template parameter) type `ALLOCATOR_TYPE` defines such a
    /// method, or 2) calling the destructor directly on `elementAddr` (and
    /// ignoring `basicAllocator`) otherwise.  The behavior is undefined
    /// unless `elementAddr` refers to a valid, constructed object.
    template <class ELEMENT_TYPE>
    static typename bsl::enable_if<
        !BloombergLP::bslma::AllocatorTraits_HasDestroyMethod<
            ALLOCATOR_TYPE,
            void,
            ELEMENT_TYPE *>::value,
        void>::type
    privateDestroy(ALLOCATOR_TYPE& basicAllocator, ELEMENT_TYPE *elementAddr);
#endif

  public:
    // PUBLIC TYPES
    typedef ALLOCATOR_TYPE                            allocator_type;
    typedef typename ALLOCATOR_TYPE::value_type       value_type;

    typedef typename
         BloombergLP::bslma::AllocatorTraits_PointerType<ALLOCATOR_TYPE>::type
                                                      pointer;
    typedef typename
    BloombergLP::bslma::AllocatorTraits_ConstPointerType<ALLOCATOR_TYPE>::type
                                                      const_pointer;
    typedef typename
     BloombergLP::bslma::AllocatorTraits_VoidPointerType<ALLOCATOR_TYPE>::type
                                                      void_pointer;
    typedef typename BloombergLP::bslma::
                    AllocatorTraits_ConstVoidPointerType<ALLOCATOR_TYPE>::type
                                                      const_void_pointer;

    typedef typename
      BloombergLP::bslma::AllocatorTraits_DifferenceType<ALLOCATOR_TYPE>::type
                                                      difference_type;
    typedef typename
            BloombergLP::bslma::AllocatorTraits_SizeType<ALLOCATOR_TYPE>::type
                                                      size_type;

#ifdef BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES
    template <class ELEMENT_TYPE>
    using rebind_alloc = typename
           BloombergLP::bslma::AllocatorTraits_RebindAlloc<ALLOCATOR_TYPE,
                                                           ELEMENT_TYPE>::type;

    template <class ELEMENT_TYPE>
    using rebind_traits = allocator_traits<rebind_alloc<ELEMENT_TYPE>>;
#else // !BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES
    template <class ELEMENT_TYPE>
    struct rebind_alloc
        : BloombergLP::bslma::AllocatorTraits_RebindAlloc<ALLOCATOR_TYPE,
                                                          ELEMENT_TYPE>::type
    {
        // Note that this class attempts to emulate an alias template, but is
        // not complete.  In general, code that must support C++03 should use
        // 'rebind_traits<ELEMENT_TYPE>::allocator_type' instead of
        // 'rebind_alloc<ELEMENT_TYPE>' because that nested typedef is the
        // preferred actual allocator type and not a subclass of the desired
        // type.

        typedef typename BloombergLP::bslma::
                AllocatorTraits_RebindAlloc<ALLOCATOR_TYPE, ELEMENT_TYPE>::type
                                                                allocator_type;

        template <typename ARG>
        rebind_alloc(const ARG& allocatorArg)
            // Convert from anything that can be used to construct the base
            // type.  This might be better if SFINAE-ed out using
            // 'is_convertible', but stressing older compilers more seems
            // unwise.
        : allocator_type(allocatorArg)
        {
        }
    };

    template <class ELEMENT_TYPE>
    struct rebind_traits : allocator_traits<typename allocator_traits::template
                                    rebind_alloc<ELEMENT_TYPE>::allocator_type>
    {
    };
#endif // !BSLS_COMPILERFEATURES_SUPPORT_ALIAS_TEMPLATES

    // Allocation functions

    /// Return `basicAllocator.allocate(n)`.
    static pointer allocate(ALLOCATOR_TYPE& basicAllocator, size_type n);

    /// Return `basicAllocator.allocate(n, hint)`.
    static pointer allocate(ALLOCATOR_TYPE&    basicAllocator,
                            size_type          n,
                            const_void_pointer hint);

    /// Invoke `basicAllocator.deallocate(elementAddr, n)`.  The behavior is
    /// undefined unless the specified `elementAddr` was returned from a
    /// prior call to the `allocate` method of an allocator that compares
    /// equal to the specified `allocator`, and has not yet been passed to a
    /// `deallocate` call of such an allocator object.
    static void deallocate(ALLOCATOR_TYPE& basicAllocator,
                           pointer         elementAddr,
                           size_type       n);

    // Element creation functions

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES // $var-args=14

    /// Construct an object of (template parameter) type `ELEMENT_TYPE` at
    /// the specified `elementAddr`, either by 1) calling the `construct`
    /// method on `basicAllocator` with `elemAddr` and the specified
    /// (variable number of) `arguments` if the (template parameter) type
    /// `ALLOCATOR_TYPE` defines such a method, or 2) forwarding the
    /// specified (variable number of) `arguments` to the constructor of
    /// `ELEMENT_TYPE` directly (and ignoring `basicAllocator`) otherwise.
    /// The behavior is undefined unless `elementAddr` refers to valid,
    /// uninitialized storage.
    template <class ELEMENT_TYPE, class... Args>
    static void construct(ALLOCATOR_TYPE&  basicAllocator,
                          ELEMENT_TYPE    *elementAddr,
                          Args&&...        arguments);
#endif

    /// Destroy the object of (template parameter) type `ELEMENT_TYPE` at
    /// the specified `elementAddr`, either by 1) calling the `destroy`
    /// method on `basicAllocator` with `elemAddr` as the sole argument if
    /// the (template parameter) type `ALLOCATOR_TYPE` defines such a
    /// method, or 2) calling the destructor directly on `elementAddr` (and
    /// ignoring `basicAllocator`) otherwise.  The behavior is undefined
    /// unless `elementAddr` refers to a valid, constructed object.
    template <class ELEMENT_TYPE>
    static void destroy(ALLOCATOR_TYPE&  basicAllocator,
                        ELEMENT_TYPE    *elementAddr);

    /// Return the largest number of `value_type` objects that could
    /// reasonably be returned by a single invocation of `allocate` for the
    /// specified `allocator`, i.e., `allocator.max_size()`.
    static size_type max_size(const ALLOCATOR_TYPE& basicAllocator)
                                                         BSLS_KEYWORD_NOEXCEPT;

    // Allocator propagation traits

    /// Return a copy of the allocator that should be used to copy-
    /// construct one container from another container whose allocator is
    /// the specified `rhs`.  If the parameterized `ALLOCATOR_TYPE` defines
    /// a method `select_on_container_copy_construction`, this function
    /// returns the result of calling that method on `rhs`; otherwise, this
    /// method enforces the default policy of propagating the allocator on
    /// copy construction, as is standard practice for standard allocators
    /// (i.e., returns `rhs`).  Note that the specialization of this class
    /// template for `bsl::allocator` (in the `bslma_bslallocator`
    /// component) provides the alternate default behavior of *not*
    /// propagating the allocator on copy construction (i.e., returning a
    /// default-constructed allocator object).
    static ALLOCATOR_TYPE
    select_on_container_copy_construction(const ALLOCATOR_TYPE& rhs);

    /// Identical to, or derived from `true_type` if two allocators of
    /// parameterized `ALLOCATOR_TYPE` always compare equal; otherwise
    /// identical to or derived from `false_type`.  This type is
    /// `ALLOCATOR_TYPE::is_always_equal` if such a type is defined, and
    /// `is_empty<ALLOCATOR_TYPE>` otherwise.
    typedef typename BloombergLP::bslma::AllocatorTraits_IsAlwaysEqual<
                             ALLOCATOR_TYPE>::type is_always_equal;

    /// Identical to, or derived from `true_type` if an allocator of
    /// parameterized `ALLOCATOR_TYPE` should be copied when a container
    /// using that `ALLOCATOR_TYPE` is copy-assigned; otherwise identical to
    /// or derived from `false_type`.  This type is
    /// `ALLOCATOR_TYPE::propagate_on_container_copy_assignment` if such a
    /// type is defined, and `false_type` otherwise.
    typedef typename BloombergLP::bslma::AllocatorTraits_PropOnCopyAssign<
                  ALLOCATOR_TYPE>::type propagate_on_container_copy_assignment;

    /// Identical to, or derived from `true_type` if an allocator of
    /// parameterized `ALLOCATOR_TYPE` should be moved when a container
    /// using that `ALLOCATOR_TYPE` is move-assigned; otherwise identical to
    /// or derived from `false_type`.  This type is
    /// `ALLOCATOR_TYPE::propagate_on_container_move_assignment` if such a
    /// type is defined, and `false_type` otherwise.
    typedef typename BloombergLP::bslma::AllocatorTraits_PropOnMoveAssign<
                  ALLOCATOR_TYPE>::type propagate_on_container_move_assignment;

    /// Identical to, or derived from `true_type` if the allocators of
    /// parameterized `ALLOCATOR_TYPE` should be swapped when containers
    /// using that `ALLOCATOR_TYPE` are swapped; otherwise identical to or
    /// derived from `false_type`.  This type is
    /// `ALLOCATOR_TYPE::propagate_on_container_swap` if such a type is
    /// defined, and `false_type` otherwise.
    typedef typename BloombergLP::bslma::AllocatorTraits_PropOnSwap<
                             ALLOCATOR_TYPE>::type propagate_on_container_swap;
};

                  // ========================================
                  // class allocator_traits<ALLOCATOR_TYPE *>
                  // ========================================

/// TBD: improve comment This is an empty class specialization of
/// `allocator_traits` for pointer types that (intentionally) does not
/// define any of the traits typedefs.  It's needed in order make
/// unambiguous function overloads that take both a standard allocator by
/// value and a `bslma::Allocator *`.  By using the typedefs defined in
/// `allocator_traits` in the signature of functions taking standard
/// allocators, we can ensure that those overloads are not considered when
/// using `bslma`-style allocators.
template <class ALLOCATOR_TYPE>
struct allocator_traits<ALLOCATOR_TYPE *> {
};

#if BSLS_COMPILERFEATURES_CPLUSPLUS >= 201703L
// Since C++17 many of the `std::allocator` members are deprecated (and removed
// in C++20).  C++23 deprecates type name members and C++26 removes them.  The
// clang compiler on Darwin warns about any mention of those names in C++17 so
// this header file was producing many warnings.  The specialization below
// "disconnects" the allocator traits of `std::allocator` from our own
// implementation and so it avoids not only warnings but possible other hassles
// as the `std::allocator` interface is morphing.
//
// Note that we are using the standard traits (in place of ours) only on C++17
// or later compilers and not C++11 (when `std::allocator_traits` appeared).
// The reason for this is that our `bsl::allocator_traits` supports
// `allocator_traits::is_always_equal` from C++17 in a backwards compatible
// manner so we cannot use pre-C++17 standard implementation that does not have
// that.
template <class ALLOCATOR_TYPE>
struct allocator_traits<std::allocator<ALLOCATOR_TYPE> >
: std::allocator_traits<std::allocator<ALLOCATOR_TYPE> > {
};

#endif

}  // close namespace bsl

// ============================================================================
//          INLINE AND TEMPLATE STATIC MEMBER FUNCTION DEFINITIONS
// ============================================================================

namespace bsl {

                           // ----------------------
                           // class allocator_traits
                           // ----------------------

template <class ALLOCATOR_TYPE>
inline
ALLOCATOR_TYPE allocator_traits<ALLOCATOR_TYPE>::selectOnCopyConstruct(
                                            const ALLOCATOR_TYPE& stdAllocator,
                                            true_type)
{
    return stdAllocator.select_on_container_copy_construction();
}

template <class ALLOCATOR_TYPE>
inline
ALLOCATOR_TYPE allocator_traits<ALLOCATOR_TYPE>::selectOnCopyConstruct(
                                            const ALLOCATOR_TYPE& stdAllocator,
                                            false_type)
{
    return stdAllocator;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE) &&                        \
    defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) &&               \
    defined(BSLS_COMPILERFEATURES_SUPPORT_VARIADIC_TEMPLATES)
template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE, class... Args>
inline
typename bsl::enable_if<
    BloombergLP::bslma::AllocatorTraits_HasConstructMethod<ALLOCATOR_TYPE,
                                                           void,
                                                           ELEMENT_TYPE *,
                                                           Args...>::value,
    void>::type
allocator_traits<ALLOCATOR_TYPE>::privateConstruct(
                                               ALLOCATOR_TYPE&  basicAllocator,
                                               ELEMENT_TYPE    *elementAddr,
                                               Args&&...        arguments)
{
    basicAllocator.construct(
        elementAddr, BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
}

template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE, class... Args>
inline
typename bsl::enable_if<
    !BloombergLP::bslma::AllocatorTraits_HasConstructMethod<ALLOCATOR_TYPE,
                                                            void,
                                                            ELEMENT_TYPE *,
                                                            Args...>::value,
    void>::type
allocator_traits<ALLOCATOR_TYPE>::privateConstruct(ALLOCATOR_TYPE&,
                                                   ELEMENT_TYPE *elementAddr,
                                                   Args&&...     arguments)
{
    ::new (static_cast<void *>(elementAddr))
        ELEMENT_TYPE(BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
}

template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE>
inline
typename bsl::enable_if<BloombergLP::bslma::AllocatorTraits_HasDestroyMethod<
                            ALLOCATOR_TYPE,
                            void,
                            ELEMENT_TYPE *>::value,
                        void>::type
allocator_traits<ALLOCATOR_TYPE>::privateDestroy(
                                               ALLOCATOR_TYPE&  basicAllocator,
                                               ELEMENT_TYPE    *elementAddr)
{
    basicAllocator.destroy(elementAddr);
}

template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE>
inline
typename bsl::enable_if<!BloombergLP::bslma::AllocatorTraits_HasDestroyMethod<
                            ALLOCATOR_TYPE,
                            void,
                            ELEMENT_TYPE *>::value,
                        void>::type
allocator_traits<ALLOCATOR_TYPE>::privateDestroy(ALLOCATOR_TYPE&,
                                                 ELEMENT_TYPE *elementAddr)
{
    elementAddr->~ELEMENT_TYPE();
}
#endif

// Allocation functions

template <class ALLOCATOR_TYPE>
inline
typename allocator_traits<ALLOCATOR_TYPE>::pointer
allocator_traits<ALLOCATOR_TYPE>::allocate(ALLOCATOR_TYPE& basicAllocator,
                                           size_type n)
{
    return basicAllocator.allocate(n);
}

template <class ALLOCATOR_TYPE>
inline
typename allocator_traits<ALLOCATOR_TYPE>::pointer
allocator_traits<ALLOCATOR_TYPE>::allocate(ALLOCATOR_TYPE&    basicAllocator,
                                           size_type          n,
                                           const_void_pointer hint)
{
    return basicAllocator.allocate(n, hint);
}

template <class ALLOCATOR_TYPE>
inline
void
allocator_traits<ALLOCATOR_TYPE>::deallocate(ALLOCATOR_TYPE& basicAllocator,
                                             pointer         elementAddr,
                                             size_type       n)
{
    basicAllocator.deallocate(elementAddr, n);
}

// ELEMENT CREATION FUNCTIONS

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE, class... Args>
inline
void
allocator_traits<ALLOCATOR_TYPE>::construct(ALLOCATOR_TYPE&  basicAllocator,
                                            ELEMENT_TYPE    *elementAddr,
                                            Args&&...        arguments)
{
#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE)
    privateConstruct(basicAllocator,
                     elementAddr,
                     BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
#else
    // Cannot sniff out whether 'basicAllocator.construct(...)' is valid in
    // C++03, but allocators are required to have a 'construct' method, so just
    // call it.
    basicAllocator.construct(
        elementAddr, BSLS_COMPILERFEATURES_FORWARD(Args, arguments)...);
#endif
}
#endif

template <class ALLOCATOR_TYPE>
template <class ELEMENT_TYPE>
inline
void
allocator_traits<ALLOCATOR_TYPE>::destroy(ALLOCATOR_TYPE& stdAllocator,
                                          ELEMENT_TYPE    *elementAddr)
{
//  For full C++11 compatibility, this should check for the well-formedness of
//  the allocator-specific code that is commented out below (via some SFINAE
//  trickery), and switch to the 'DestructionUtil' implementation only if the
// 'destroy' member function is not available.

//    allocator.destroy(elementAddr);
#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE) &&                       \
    defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES)
    privateDestroy(stdAllocator, elementAddr);
#else
    elementAddr->~ELEMENT_TYPE();
    (void) stdAllocator;
#endif
}

template <class ALLOCATOR_TYPE>
inline
typename allocator_traits<ALLOCATOR_TYPE>::size_type
allocator_traits<ALLOCATOR_TYPE>::max_size(
                    const ALLOCATOR_TYPE& basicAllocator) BSLS_KEYWORD_NOEXCEPT
{
#if defined(BSLS_COMPILERFEATURES_SUPPORT_DECLTYPE)
    return BloombergLP::bslma::
         AllocatorTraits_CallMaxSize<ALLOCATOR_TYPE>::max_size(basicAllocator);
#else
    // Cannot sniff out whether 'basicAllocator.max_size()' is valid in C++03,
    // but for now require that allocators have a 'max_size' method and just
    // call it.
    return basicAllocator.max_size();
#endif
}

template <class ALLOCATOR_TYPE>
inline
ALLOCATOR_TYPE
allocator_traits<ALLOCATOR_TYPE>::select_on_container_copy_construction(
                                                     const ALLOCATOR_TYPE& rhs)
{
    return selectOnCopyConstruct(rhs, DelegateSelectMethod());
}

}  // close namespace bsl

#endif // End C++11 code

#endif // ! defined(INCLUDED_BSLMA_ALLOCATORTRAITS)

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
