// bslstl_pair.h                                                      -*-C++-*-
#ifndef INCLUDED_BSLSTL_PAIR
#define INCLUDED_BSLSTL_PAIR

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

//@PURPOSE: Provide a simple `struct` with two members that may use allocators.
//
//@CLASSES:
//  bsl::pair: pair of values, each of which may use a `bslma::Allocator`
//
//@CANONICAL_HEADER: bsl_utility.h
//
//@DESCRIPTION: `bsl::pair` is an allocator-aware version of `std::pair`.
// The `bsl::pair` class template is instantiated on two types, `T1` and `T2`,
// and provides two public data members, `first` and `second` of types `T1` and
// `T2`, respectively.  Each data member might or might not allocate memory
// using `bslma::Allocator`.  Its interface is identical to `std::pair` except
// that it has constructors that take optional allocator arguments to correctly
// construct the member variables.  For example, a
// `bsl::pair<bsl::string, int>` has member `first` of type `bsl::string` and
// `second` of type `int`.  A client can pass a `bslma::Allocator` pointer to
// the pair constructor and the constructor will pass it through to the `first`
// member.  Similarly, the copy constructor takes an optional
// `bslma::Allocator` pointer and copy-constructs the `first` member using that
// allocator.
//
// `bsl::pair` is unusual in that its data members, `first` and `second`, are
// public.  Once constructed, a client program accesses these members directly.
// This part of the interface is identical to `std::pair`, for which
// `bsl::pair` is intended to be a drop-in replacement.
//
// `bsl::pair` has four constructors: a default constructor that
// default-constructs the two data members, a copy constructor that
// copy-constructs each data member, a constructor taking two arguments of type
// `T1` and `T2`, which are used to copy-construct `first` and `second`
// respectively, and a conversion constructor template for converting from a
// `bsl::pair` of different types, `PARAM_1` and `PARAM_2`, provided `PARAM_1`
// is convertible to `T1` and `PARAM_2` is convertible to `T2`.  Each
// constructor also has an optional `bslma::Allocator` pointer argument.  If
// neither `T1` nor `T2` use `bslma::Allocator`, this argument is ignored.
// Otherwise, either `first` or `second`, or both, depending on whether each
// type uses `bslma::Allocator`, will be passed the `bslma::Allocator*`
// argument during construction.  Whether or not a type uses `bslma::Allocator`
// is determined by querying the `bslma::UsesBslmaAllocator` trait for that
// type.  This component also defines a full set of equality and relational
// operators that can be instantiated if `T1` and `T2` both provide those
// operators.
//
// A `bsl::pair` declares a set of associated type traits that are computed
// from the type traits of `T1` and `T2`.  For each supported type trait, a
// given specialization of `bsl::pair` has that trait if and only if *both*
// `T1` and `T2` have that trait.  Supported traits are:
// ```
// bslmf::IsBitwiseMoveable
// bslmf::IsBitwiseEqualityComparable
// bsl::is_trivially_copyable
// bsl::is_trivially_default_constructible
// ```
// In addition, a `bsl::pair` specialization has the
// `bslma::UsesBslmaAllocator` trait if *either* `T1` or `T2` have that trait,
// or both.
//
///Enhancements Enabled by Modern Language Standards
///-------------------------------------------------
// Language standards after the first (C++98) and its corrigendum (C++03) have
// added code C++ language features, type traits as well as requirements that
// affect the exact interface `pair` provides.  This section describes such
// enhancements as they become available.
//
///Conditional Default Constructor (C++11)
///- - - - - - - - - - - - - - - - - - - -
// C++11 has introduced type traits, many of which needs compiler backing to be
// (automatically implementable) and also introduced "defaulted" (special
// member) functions that generate code without making mistakes.  Before C++11
// it was not possible to determine if a type had a default constructor in a
// non-intrusive manner.  C++11 makes it possible using <type_traits> to
// determine that, and `= default` makes it possible to create special member
// functions that exists only when they can be implemented properly.
//
// Hence, when using compilers with a reasonably complete implementation of
// C++11 we implement the default constructor of `pair` only if both types
// inside the pair type have a default constructor.  Note that it means that
// when using C++11 a `bsl::pair` that stores a reference type (such as
// `int&`), or any other type that cannot be default constructed using the
// syntax `T v{};`, will cause `pair` to neither declare nor define a default
// constructor.  So from C++11 onwards a type of `bsl::pair<T1, T2>` will have
// a default constructor only if both types `T1` and `T2` are default
// constructible.  Otherwise, `pair` will have a default constructor that gives
// a compile-error (only) if called.
//
///Usage
///-----
// A `bsl::pair` is a very simple object when used without allocators.  Our
// usage example concentrates on the use of allocators with `bsl::pair`.
// First, we create a utility function that copies a null-terminated string
// into memory allocated from a supplied allocator:
// ```
// char *myStrDup(const char *s, bslma::Allocator *basicAllocator)
//     // Copy the specified null-terminated string 's' into memory allocated
//     // from the specified 'basicAllocator'
// {
//     char *result = (char*) basicAllocator->allocate(std::strlen(s) + 1);
//     return std::strcpy(result, s);
// }
// ```
// We create a simple string class that holds strings allocated from a supplied
// allocator.  It uses `myStrDup` (above) in its implementation:
// ```
// class my_String {
//     // Simple string class that uses a 'bslma::Allocator' allocator.
//
//     bslma::Allocator *d_allocator_p;
//     char             *d_data;
//
//   public:
//     BSLMF_NESTED_TRAIT_DECLARATION(my_String, bslma::UsesBslmaAllocator);
//
//     explicit my_String(bslma::Allocator *basicAllocator = 0);
//         // Construct an empty string using the optionally-specified
//         // allocator 'basicAllocator'.
//
//     my_String(const char* s, bslma::Allocator *alloc = 0);      // IMPLICIT
//         // Construct a string with contents specified in 's' using the
//         // optionally-specified allocator 'basicAllocator'.
//
//     my_String(const my_String& rhs, bslma::Allocator *alloc = 0);
//         // Construct a copy of the specified 'rhs' string using the
//         // optionally-specified allocator 'basicAllocator'.
//
//     ~my_String();
//         // Destroy this string.
//
//     my_String& operator=(const my_String& rhs);
//         // Copy specified 'rhs' string value to this string.
//
//     const char* c_str() const;
//         // Return the null-terminated character array for this string.
//
//     bslma::Allocator *allocator() const;
//         // Return the allocator used to construct this string or, if no
//         // allocator was specified at construction, the default allocator
//         // at the time of construction.
// };
//
// bool operator==(const my_String& str1, const my_String& str2)
// {
//     return 0 == std::strcmp(str1.c_str(), str2.c_str());
// }
//
// bool operator==(const my_String& str, const char *p)
// {
//     return 0 == std::strcmp(p, str.c_str());
// }
//
// bool operator==(const char *p, const my_String& str)
// {
//     return str == p;
// }
//
// bool operator!=(const my_String& str1, const my_String& str2)
// {
//     return ! (str1 == str2);
// }
//
// bool operator!=(const my_String& str, const char *p)
// {
//     return ! (str == p);
// }
//
// bool operator!=(const char *p, const my_String& str)
// {
//     return ! (str == p);
// }
//
// bool operator<(const my_String& str1, const my_String& str2)
// {
//     return std::strcmp(str1.c_str(), str2.c_str()) < 0;
// }
//
// my_String::my_String(bslma::Allocator *basicAllocator)
// : d_allocator_p(bslma::Default::allocator(basicAllocator)), d_data(0)
// {
//     d_data = myStrDup("", d_allocator_p);
// }
//
// my_String::my_String(const char *s, bslma::Allocator *basicAllocator)
// : d_allocator_p(bslma::Default::allocator(basicAllocator)), d_data(0)
// {
//     d_data = myStrDup(s, d_allocator_p);
// }
//
// my_String::my_String(const my_String&  rhs,
//                      bslma::Allocator *basicAllocator)
// : d_allocator_p(bslma::Default::allocator(basicAllocator)), d_data(0)
// {
//     d_data = myStrDup(rhs.d_data, d_allocator_p);
// }
//
// my_String::~my_String()
// {
//     d_allocator_p->deallocate(d_data);
// }
//
// my_String& my_String::operator=(const my_String& rhs)
// {
//     if (this != &rhs) {
//         d_allocator_p->deallocate(d_data);
//         d_data = myStrDup(rhs.d_data, d_allocator_p);
//     }
//     return *this;
// }
//
// const char *my_String::c_str() const
// {
//     return d_data;
// }
//
// bslma::Allocator *my_String::allocator() const
// {
//     return d_allocator_p;
// }
// ```
// Our main program creates a mapping from strings to integers.  Each node of
// the mapping consists of a `bsl::pair<my_String, int>`.  The program
// allocates memory from a test allocator in order to ensure that there are no
// leaks:
// ```
// int main()
// {
//     typedef bsl::pair<my_String, int> Node;
//
//     Node *mapping[3];
//     bslma::TestAllocator alloc;
// ```
// When constructing a `Node`, an allocator is supplied in addition to
// parameters for the `first` and `second` data members.
// ```
// {
//     mapping[0] = new(basicAllocator) Node("One", 1, &basicAllocator);
//     mapping[1] = new(basicAllocator) Node("Three", 3, &basicAllocator);
//     mapping[2] = new(basicAllocator) Node("Two", 2, &basicAllocator);
//     // Temporaries get destroyed here, even on broken compilers.
// }
//
// assert("One" == mapping[0]->first);
// assert(1 == mapping[0]->second);
// assert("Three" == mapping[1]->first);
// assert(3 == mapping[1]->second);
// assert("Two" == mapping[2]->first);
// assert(2 == mapping[2]->second);
//
// assert(6 == alloc.numBlocksInUse());
// ```
// Clean up at end.
// ```
//     alloc.deleteObjectRaw(mapping[0]);
//     alloc.deleteObjectRaw(mapping[1]);
//     alloc.deleteObjectRaw(mapping[2]);
//
//     assert(0 == alloc.numBlocksInUse());
//
//     return 0;
// }
// ```

#include <bslscm_version.h>

#include <bslstl_hash.h>

#include <bslalg_synththreewayutil.h>

#include <bslh_hash.h>

#include <bslma_allocator.h>
#ifndef BDE_OMIT_INTERNAL_DEPRECATED
# include <bslma_managedptr_pairproxy.h>
#endif // BDE_OMIT_INTERNAL_DEPRECATED
#include <bslma_usesbslmaallocator.h>

#include <bslmf_addlvaluereference.h>
#include <bslmf_allocatorargt.h>
#include <bslmf_conditional.h>
#include <bslmf_enableif.h>
#include <bslmf_integersequence.h>
#include <bslmf_isbitwisecopyable.h>
#include <bslmf_isbitwiseequalitycomparable.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isconvertible.h>
#include <bslmf_isnothrowswappable.h>
#include <bslmf_ispair.h>
#include <bslmf_issame.h>
#include <bslmf_isswappable.h>
#include <bslmf_istriviallycopyable.h>
#include <bslmf_istriviallydefaultconstructible.h>
#include <bslmf_makeintegersequence.h>
#include <bslmf_movableref.h>
#include <bslmf_removecv.h>
#include <bslmf_removecvref.h>
#include <bslmf_usesallocatorargt.h>
#include <bslmf_util.h>

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

#include <algorithm>    // `std::swap` for C++03
#include <cstddef>      // 'std::size_t'
#include <utility>      // 'std::pair' and (in C++11 mode) 'std::swap'

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
# include <tuple>       // 'std::tuple'
#endif

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP20_CONCEPTS
# include <concepts>
#endif

// ============================================================================
//                  COMPONENT SPECIFIC FEATURE TESTS
// ============================================================================

#if !defined(BSLS_COMPILERFEATURES_FULL_CPP11)                                 \
 || (defined(BSLS_PLATFORM_CMP_MSVC)                                           \
     && (BSLS_COMPILERFEATURES_CPLUSPLUS < 201703L                             \
         || BSLS_PLATFORM_CMP_VERSION <= 1916))
/// The 'bsl::is_swappable' trait requires C++11 features, notably `decltype`,
/// so cannot be back-ported to C++03.  In a separate issue, early versions of
/// MSVC, up to and including MSVC 2017 are unable to perform the required
/// template argument deductions to enable the use of 'bsl::is_swappable'
/// within the SFINAE test for 'swap()', and later version of MSVC retain this
/// issue unless C++17 or later is enabled.   In all cases we fall back on the
/// previous, pre-C++17 implementation which simply assumes swappability and
/// fails to compile when instantiated with a non-swappable type.
# define BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE 1
#endif

// ============================================================================
//                      SIMPLIFYING ASSUMPTIONS
// ============================================================================

/// `bslstl_pair` is a surprisingly complicated component.  Trying to expose as
/// much of its functionality as possible while using fine-grained feature
/// detection macros for every incremental feature added to the C++ standard
/// raises that complexity too far, so we will assume that C++11 is fully
/// supported if compiled with C++11 (or later) support.

/// What this means in practice is that any C++11 extensions backported as
/// extensions to C++03 will not be available in this component, potentially
/// making that code slightly less efficient.  Meanwhile, all the C++11
/// features can be used in code protected by a check for the macro
/// `BSLS_COMPILERFEATURES_FULL_CPP11` without the complexity of nested checks
/// to combine multiple features, and allowing clear use of C++11 syntax
/// without the obfuscation of `BSLS_KEYWORD` macros.

/// We will also prefer `bsl` type traits over `std` type traits as `bsl`
/// guarantees the simplifying alias templates will be available.  However we
/// cannot use variable templates without C++14 support for variable templates
/// in the language.

/// Library assumptions:
/// --------------------

#if  defined(BSLS_COMPILERFEATURES_FULL_CPP11)              \
 && !defined(BSLS_LIBRARYFEATURES_HAS_CPP14_INTEGER_SEQUENCE)
#   error piecewise construtors assume this essential machinery from C++14
    // Note that this facility has been backported to C++11 for all library
    // implementations that we have tested and that also support
    // `BSLS_COMPILERFEATURES_FULL_CPP11`.
#endif

namespace BloombergLP {
namespace bslstl {

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
                        // ========================
                        // trait Pair_IndexSequence
                        // ========================

/// `Pair_IndexSequence` is an alias to the `bslmf::IntegerSequence` class
/// template specialized for the common case of the integer sequence were the
/// element type `T` is `std::size_t`.
template <std::size_t... INTS>
using Pair_IndexSequence = bslmf::IntegerSequence<std::size_t, INTS...>;

/// `Pair_MakeIndexSequence` is an alias template to the
/// `bslmf::MakeIntegerSequence` meta-function specialized for the common case
/// of the integer sequence were the element type `T` is `std::size_t`.
template <std::size_t N>
using Pair_MakeIndexSequence = bslmf::MakeIntegerSequence<std::size_t, N>;
#endif

}  // close package namespace
}  // close enterprise namespace

namespace bsl {
                        // =========================
                        // trait Pair_AllocatorIdiom
                        // =========================

/// Type tag for types that do not take a `bslma::Allocator*` constructor
/// argument.
typedef bsl::integral_constant<int, 0> Pair_BslmaIdiomNone;

/// Type tag for types that take a `bslma::Allocator*` as the last argument of
/// their constructors.
typedef bsl::integral_constant<int, 1> Pair_BslmaIdiomAtEnd;

/// Type tag for types that take a `bslma::Allocator*` as the second argument
/// of their constructors, following an argument of type
/// `bsl::allocator_arg_t`.
typedef bsl::integral_constant<int, 2> Pair_BslmaIdiomAllocatorArgT;

/// This component-private meta-function determines whether the specified
/// `TYPE` template parameter takes a `bslma::Allocator*` constructor argument
/// and, if so, whether that argument is at the end of the argument list or at
/// the beginning of the argument list following an argument of type
/// `bsl::allocator_arg_t`.  This type derived from
/// `bsl::integral_constant<int, N>` where `N` is the number of additional
/// parameters required to pass an allocator to a constructor using the chosen
/// idiom.
template <class TYPE>
struct Pair_BslmaIdiom : bsl::conditional
         < !BloombergLP::bslma::UsesBslmaAllocator<TYPE>::value
         , Pair_BslmaIdiomNone
         , typename
           bsl::conditional< BloombergLP::bslmf::UsesAllocatorArgT<TYPE>::value
                           , Pair_BslmaIdiomAllocatorArgT
                           , Pair_BslmaIdiomAtEnd
                           >::type
         >::type
{
};

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
/// This component-private component-private meta-function determines the
/// number of elements in a tuple containing parameters for piecewise
/// construction of a `bsl::pair` member having the specified `TYPE`.  Result
/// value depends on the `TYPE`, whether it takes a `bslma::Allocator*`
/// constructor argument and, if so, whether that argument follows an argument
/// of type `bsl::allocator_arg_t`.
template <class TYPE, class ... ARGS>
struct Pair_ConstructionParametersPackLength : integral_constant<int,
                               sizeof...(ARGS) + Pair_BslmaIdiom<TYPE>::value >
{
};
#endif

                        // ===================
                        // struct Pair_ImpUtil
                        // ===================
/// This 'struct' provides a namespace for utility functions used to creating a
/// tuple, containing arguments for the constructor of pair element in the
/// process of 'bsl::pair' piecewise construction.
struct Pair_ImpUtil {

    // CLASS METHODS
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct and return by value a tuple, containing arguments for the
    /// corresponding  constructor of (template parameter) `TYPE`, forwarding
    /// in order the elements in the specified `tpl` and discarding the
    /// specified `alloc`, because `TYPE` does not support `bslma`-style
    /// allocators.  This method provides the no-throw exception-safety
    /// guarantee.
    template <class ... ARGS>
    static
    std::tuple<ARGS...>
    concatAllocator(std::tuple<ARGS...>&&          tpl,
                    BloombergLP::bslma::Allocator* alloc,
                    bsl::Pair_BslmaIdiomNone);

    /// Construct and return by value a tuple, containing arguments for the
    /// corresponding  constructor of (template parameter) `TYPE`, forwarding
    /// in order the elements in the specified `tpl` and appending the
    /// specified `alloc`, because `TYPE` takes a `bslma`-style allocator as
    /// the last constructor argument.  This method provides the no-throw
    /// exception-safety guarantee.
    template <class ... ARGS>
    static
    std::tuple<ARGS..., BloombergLP::bslma::Allocator *>
    concatAllocator(std::tuple<ARGS...>&&          tpl,
                    BloombergLP::bslma::Allocator* alloc,
                    bsl::Pair_BslmaIdiomAtEnd);

    /// Construct and return by value a tuple, containing arguments for the
    /// corresponding constructor of (template parameter) `TYPE`, forwarding in
    /// order the elements in the specified `tpl` preceded by
    /// `bsl::allocator_arg` object and the specified `alloc`, because `TYPE`
    /// takes a `bslma`-style allocator as the second constructor argument
    /// preceded by `bsl::allocator_arg`.  This method provides the no-throw
    /// exception-safety guarantee.
    template <class ... ARGS>
    static
    std::tuple<bsl::allocator_arg_t,
               BloombergLP::bslma::Allocator *,
               ARGS...>
    concatAllocator(std::tuple<ARGS...>&&          tpl,
                    BloombergLP::bslma::Allocator* alloc,
                    bsl::Pair_BslmaIdiomAllocatorArgT);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11) &&                        \
    defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
    // Utility function to determine whether 'swap()' is 'noexcept (true)' when
    // called with the specified (template) arguments 'TYPE1' and 'TYPE2'.
    // This function is defined only on platforms where 'noexcept' is supported
    // but the newer, C++17 compatible trait 'bsl::is_nothrow_swappable' is not
    // available.
    template <class TYPE1, class TYPE2>
    static constexpr bool hasNothrowSwap()
    {
        using std::swap;
        using U = BloombergLP::bslmf::Util;
        return noexcept(swap(U::declval<TYPE1&>(), U::declval<TYPE1&>())) &&
               noexcept(swap(U::declval<TYPE2&>(), U::declval<TYPE2&>()));
    }
#endif
};

                             // =================
                             // struct Pair_First
                             // =================

/// This component-private `class` holds the `first` data member of a `pair`
/// and constructs it appropriately.  Note the partial specializations below
/// for the cases that (template parameter) 'TYPE' is a reference.
template <class TYPE>
struct Pair_First {

  private:
    // PRIVATE TYPES
    typedef typename Pair_BslmaIdiom<TYPE>::type FirstBslmaIdiom;

  protected:
    // PROTECTED DATA
    TYPE first;

    // CREATORS

    /// We must rely on implicitly declared special member functions to
    /// correctly support element types like `bslma::ManagedPtr` that have an
    /// unconventional copy constructor taking its argument by non-`const`
    /// reference.  If we were to expliclty default these members then we would
    /// be forcing the copy constructor parameter to be `const TYPE &` whereas
    /// the implicitly declared constructor parameter will be `TYPE &`.  We
    /// must then allow the move operations to be implicitly declared as if we
    /// explicitly default them then the copy operations would be deleted.  As
    /// these operations are implicitly declared they will similarly be
    /// trivial, constexpr, noexcept, explicit, deleted, or (in the case of
    /// move operations) omitted according to the definition of (template
    /// parameter) `TYPE`.  Note that, to preserve its intended use as a
    /// structural base class, the rest of the class is declared as protected
    /// while the implcitly declared members will be public.  Note that we
    /// *must* define the default constructor as a) it would not be declared
    /// due to the declarations of additional constructors, and b) we need to
    /// ensure that trivial types are value initialized rather than not
    /// initialized.  Finally note that as of C++20, an explicitly defaulted
    /// special member would handle the case of non-`const` qualified copy
    /// operations correctly; we retain this convention while we continue to
    /// support C++17 and earlier dialects, but also embracing "rule of zero"
    /// as a widely regarded best practice.
    //! Pair_First(const Pair_First&) = default;
    //! Pair_First(Pair_First&&) = default;
    //! Pair_First& operator=(const Pair_First&) = default;
    //! Pair_First& operator=(Pair_First&&) = default;
    //! ~Pair_First() = default;

#if defined(BSLS_COMPILERFEATURES_SUPPORT_CONCEPTS)
    /// Value-intialize the 'first' member of a 'pair'.  This constructor is
    /// declared only if (template parameter) `TYPE` is default constructible.
    constexpr Pair_First()
        requires std::is_default_constructible_v<TYPE>
    : first()
    {
        // This constructor template must be defined inline inside the
        // class definition, as Microsoft Visual C++ does not recognize the
        // definition as matching this signature when placed out-of-line.
    }
#elif defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Value-intialize the 'first' member of a 'pair'.  This constructor is
    /// declared only if (template parameter) `TYPE` is default constructible.
    template <class BSLSTL_DUMMY = TYPE,
              class = bsl::enable_if_t<
                           std::is_default_constructible<BSLSTL_DUMMY>::value>>
    constexpr Pair_First()
    : first()
    {
        // This constructor template must be defined inline inside the
        // class definition, as Microsoft Visual C++ does not recognize the
        // definition as matching this signature when placed out-of-line.
    }
#else
    /// Value-intialize the 'first' member of a 'pair'.
    Pair_First();
#endif

    /// Construct the `first` member of a `pair`, using the specified
    /// `basicAllocator` to supply memory.  Note that exactly one of these
    /// three constructors is enabled at compile-time for (template
    /// parameter) type `TYPE` based on the following respective criteria:
    /// 1) `TYPE` does not support `bslma`-style allocators, 2) `TYPE`
    /// takes a `bslma`-style allocator as the last constructor argument,
    /// and 3) `TYPE` takes a `bslma`-style allocator as the second
    /// constructor argument preceded by `bsl::allocator_arg`.
    Pair_First(BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
    Pair_First(BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAtEnd);
    Pair_First(BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAllocatorArgT);

    /// Construct the `first` member from the specified non-modifiable
    /// `value`, without specifying an allocator.
    BSLS_KEYWORD_CONSTEXPR
    explicit Pair_First(
                   typename bsl::add_lvalue_reference<const TYPE>::type value);

    /// Construct the 'first' member of a 'pair' from the specified 'value'
    /// without specifying an allocator.  This function (perfectly) forwards
    /// 'value' to the constructor of (template parameter) 'TYPE'.  Note that
    /// this constructor is constrained to avoid accidentally becoming a
    /// stronger match for the move and copy constructors.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM,
              class = bsl::enable_if_t<
                 !bsl::is_same<Pair_First, bsl::remove_cvref_t<PARAM>>::value>>
    constexpr explicit Pair_First(PARAM&& value);
#else   // no C++11 support
    template <class PARAM>
    explicit Pair_First(const PARAM& value);
    template <class PARAM>
    explicit Pair_First(PARAM& value);
#endif

    /// Construct the 'first' member of a 'pair' from the specified 'value',
    /// using the specified 'basicAllocator' to supply memory.  This
    /// function (perfectly) forwards 'value' to the constructor of
    /// (template parameter) 'TYPE'.  Note that exactly one of these three
    /// constructors is enabled at compile-time for (template parameter)
    /// type 'TYPE' based on the following respective criteria:
    ///..
    ///  1) 'TYPE' does not support 'bslma'-style allocators, or
    ///  2) 'TYPE' takes a 'bslma'-style allocator as the last constructor
    ///      argument, or
    ///  3) 'TYPE' takes a 'bslma'-style allocator as the second constructor
    ///     argument preceded by 'bsl::allocator_arg'.
    ///..
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM>
    Pair_First(PARAM&&                        value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_First(PARAM&&                        value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_First(PARAM&&                        value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAllocatorArgT);
#else
    template <class PARAM>
    Pair_First(const PARAM&                   value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_First(PARAM&                         value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_First(const PARAM&                   value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_First(PARAM&                         value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_First(const PARAM&                   value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAllocatorArgT);
    template <class PARAM>
    Pair_First(PARAM&                         value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomAllocatorArgT);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `first` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ...ARGS, size_t ...I>
    Pair_First(std::tuple<ARGS...>&& argsPack,
               BloombergLP::bslstl::Pair_IndexSequence<I...>);
#endif
};

/// This component-private `class` holds the `first` data member of a `pair`
/// when that member is an lvalue-reference.  Note that the C++ Standard
/// defines the assignment operator to assign through the reference in this
/// case, where the implictly declared assignment operator would be deleted.
template <class TYPE>
struct Pair_First<TYPE&> {

  protected:
    // PROTECTED DATA
    TYPE& first;

    // CREATORS

    /// Bind to `first` the entity of (template parameter) `TYPE` that is
    /// bound to the `first` reference of `other`.  Note that, unlike the
    /// primary template, these constructors must be declared and defaulted
    /// as the user-declared copy-assignment operator would inhibit the
    /// declaration of the move constructor, which in turn would mean that
    /// the implicitly declared constructors for the derived `pair` class
    /// would be just the copy constructor taking a `const` reference, even
    /// when the second element is move-only.  Declaring the move constructor
    /// deletes the copy constructor unless it too is declared and defaulted.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    Pair_First(const Pair_First& other) = default;
    Pair_First(Pair_First&& other) = default;
#endif

    /// Bind the specified `value` into the `first` reference-member.
    BSLS_KEYWORD_CONSTEXPR
    explicit Pair_First(TYPE& value);

    /// Bind the specified 'value' into the 'first' reference-member.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM, class = bsl::enable_if_t<
                                   std::is_constructible<TYPE, PARAM>::value>>
    constexpr
    explicit Pair_First(PARAM&& value);
#else   // no C++11 support
    template <class PARAM>
    explicit Pair_First(const PARAM& value);   // for derived-to-const-base-ref
    template <class PARAM>
    explicit Pair_First(PARAM& value);
#endif

    /// Bind the specified 'value' into the 'first' reference-member.  The
    /// specified 'basicAllocator' is not used.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM>
    constexpr
    Pair_First(PARAM&&                        value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
#else   // no C++11 support
    template <class PARAM>
    Pair_First(const PARAM&                   value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_First(PARAM&                         value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `first` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ARG>
    constexpr
    Pair_First(std::tuple<ARG>&& arg,
               BloombergLP::bslstl::Pair_IndexSequence<0u>);
#endif

    // MANIPULATORS

    /// Assign to the object bound to `first` the value of the object bound
    /// to 'rhs.first', and return a reference offering modifiable access to
    /// this object.  Note that this operations assigns through the reference,
    /// it does not rebind the reference.
    Pair_First& operator=(const Pair_First& rhs) BSLS_KEYWORD_NOEXCEPT;
};

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
/// This component-private `class` holds the `first` data member of a `pair`
/// when that member is an rvalue-reference.  Note that the C++ Standard
/// defines the assignment operator to assign through the reference in this
/// case, where the implictly declared assignment operator would be deleted.
template <class TYPE>
struct Pair_First<TYPE&&> {

  protected:
    // PROTECTED DATA
    TYPE&& first;

    // CREATORS

    /// Bind to `first` the entity of (template parameter) `TYPE` that is
    /// bound to the `first` reference of `other`.  Note that, unlike the
    /// primary template, these constructors must be declared and defaulted
    /// as the user-declared copy-assignment operator would inhibit the
    /// declaration of the move constructor, which in turn would mean that
    /// the implicitly declared constructors for the derived `pair` class
    /// would be just the copy constructor taking a `const` reference, even
    /// when the second element is move-only.  Declaring the move constructor
    /// deletes the copy constructor unless it too is declared and defaulted.
    Pair_First(const Pair_First&) = default;
    Pair_First(Pair_First&&) = default;

    /// Bind the specified `value` into the `first` reference-member.
    constexpr
    explicit Pair_First(TYPE&& value);

    /// Bind the specified 'value' into the 'first' reference-member.
    template <class PARAM, class = bsl::enable_if_t<
                                   std::is_constructible<TYPE, PARAM>::value>>
    constexpr
    explicit Pair_First(PARAM&& value);

    /// Bind the specified 'value' into the 'first' reference-member.  The
    /// specified 'basicAllocator' is not used.
    template <class PARAM>
    constexpr
    Pair_First(PARAM&&                        value,
               BloombergLP::bslma::Allocator *basicAllocator,
               Pair_BslmaIdiomNone);

# if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `first` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ARG>
    constexpr
    Pair_First(std::tuple<ARG>&& arg,
               BloombergLP::bslstl::Pair_IndexSequence<0u>);
# endif

    // MANIPULATORS

    /// Assign to the object bound to `first` the value of the object bound
    /// to 'rhs.first', and return a reference offering modifiable access to
    /// this object.  Note that this operations assigns through the reference,
    /// it does not rebind the reference.
    Pair_First& operator=(const Pair_First& rhs) noexcept;
};
#endif

                             // ==================
                             // struct Pair_Second
                             // ==================

/// This component-private `class` holds the `second` data member of a `pair`
/// and constructs it appropriately.  Note the partial specializations below
/// for the cases that (template parameter) 'TYPE' is a reference.
template <class TYPE>
struct Pair_Second {

  private:
    // PRIVATE TYPES
    typedef typename Pair_BslmaIdiom<TYPE>::type SecondBslmaIdiom;

  protected:
    // PROTECTED DATA
    TYPE second;

    // CREATORS

    /// We must rely on implicitly declared special member functions to
    /// correctly support element types like `bslma::ManagedPtr` that have an
    /// unconventional copy constructor taking its argument by non-`const`
    /// reference.  If we were to expliclty default these members then we would
    /// be forcing the copy constructor parameter to be `const TYPE &` whereas
    /// the implicitly declared constructor parameter will be `TYPE &`.  We
    /// must then allow the move operations to be implicitly declared as if we
    /// explicitly default them then the copy operations would be deleted.  As
    /// these operations are implicitly declared they will similarly be
    /// trivial, constexpr, noexcept, explicit, deleted, or (in the case of
    /// move operations) omitted according to the definition of (template
    /// parameter) `TYPE`.  Note that, to preserve its intended use as a
    /// structural base class, the rest of the class is declared as protected
    /// while the implcitly declared members will be public.  Note that we
    /// *must* define the default constructor as a) it would not be declared
    /// due to the declarations of additional constructors, and b) we need to
    /// ensure that trivial types are value initialized rather than not
    /// initialized.  Finally note that as of C++20, an explicitly defaulted
    /// special member would handle the case of non-`const` qualified copy
    /// operations correctly; we retain this convention while we continue to
    /// support C++17 and earlier dialects, but also embracing "rule of zero"
    /// as a widely regarded best practice.
    //! Pair_Second(const Pair_Second&) = default;
    //! Pair_Second(Pair_Second&&) = default;
    //! Pair_Second& operator=(const Pair_Second&) = default;
    //! Pair_Second& operator=(Pair_Second&&) = default;
    //! ~Pair_Second() = default;

#if defined(BSLS_COMPILERFEATURES_SUPPORT_CONCEPTS)
    /// Value-intialize the 'second' member of a 'pair'.  This constructor is
    /// declared only if (template parameter) `TYPE` is default constructible.
    constexpr Pair_Second()
        requires std::is_default_constructible_v<TYPE>
    : second()
    {
        // This constructor template must be defined inline inside the
        // class definition, as Microsoft Visual C++ does not recognize the
        // definition as matching this signature when placed out-of-line.
    }
#elif defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Value-intialize the 'second' member of a 'pair'.  This constructor is
    /// declared only if (template parameter) `TYPE` is default constructible.
    template <class BSLSTL_DUMMY = TYPE,
              class = bsl::enable_if_t<
                           std::is_default_constructible<BSLSTL_DUMMY>::value>>
    constexpr Pair_Second()
    : second()
    {
        // This constructor template must be defined inline inside the
        // class definition, as Microsoft Visual C++ does not recognize the
        // definition as matching this signature when placed out-of-line.
    }
#else
    /// Value-intialize the 'second' member of a 'pair'.
    Pair_Second();
#endif

    /// Construct the `second` member of a `pair`, using the specified
    /// `basicAllocator` to supply memory.  Note that exactly one of these
    /// three constructors is enabled at compile-time for (template
    /// parameter) type `TYPE` based on the following respective criteria:
    /// 1) `TYPE` does not support `bslma`-style allocators, 2) `TYPE`
    /// takes a `bslma`-style allocator as the last constructor argument,
    /// and 3) `TYPE` takes a `bslma`-style allocator as the second
    /// constructor argument preceded by `bsl::allocator_arg`.
    Pair_Second(BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
    Pair_Second(BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAtEnd);
    Pair_Second(BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAllocatorArgT);

    /// Construct the `second` member from the specified non-modifiable
    /// `value`, without specifying an allocator.
    BSLS_KEYWORD_CONSTEXPR
    explicit Pair_Second(
                   typename bsl::add_lvalue_reference<const TYPE>::type value);

    /// Construct the 'second' member of a 'pair' from the specified 'value'
    /// without specifying an allocator.  This function (perfectly) forwards
    /// 'value' to the constructor of (template parameter) 'TYPE'.  Note that
    /// this constructor is constrained to avoid accidentally becoming a
    /// stronger match for the move and copy constructors.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM,
              class = bsl::enable_if_t<
                        !bsl::is_same<Pair_Second,
                                      bsl::remove_cvref_t<PARAM>>::value>>
    constexpr explicit Pair_Second(PARAM&& value);
#else   // no C++11 support
    template <class PARAM>
    explicit Pair_Second(const PARAM& value);
    template <class PARAM>
    explicit Pair_Second(PARAM& value);
#endif

    /// Construct the 'second' member of a 'pair' from the specified 'value',
    /// using the specified 'basicAllocator' to supply memory.  This
    /// function (perfectly) forwards 'value' to the constructor of
    /// (template parameter) 'TYPE'.  Note that exactly one of these three
    /// constructors is enabled at compile-time for (template parameter)
    /// type 'TYPE' based on the following respective criteria:
    ///..
    ///  1) 'TYPE' does not support 'bslma'-style allocators, or
    ///  2) 'TYPE' takes a 'bslma'-style allocator as the last constructor
    ///      argument, or
    ///  3) 'TYPE' takes a 'bslma'-style allocator as the second constructor
    ///     argument preceded by 'bsl::allocator_arg'.
    ///..
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM>
    Pair_Second(PARAM&&                        value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_Second(PARAM&&                        value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_Second(PARAM&&                        value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAllocatorArgT);
#else
    template <class PARAM>
    Pair_Second(const PARAM&                   value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_Second(PARAM&                         value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_Second(const PARAM&                   value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_Second(PARAM&                         value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAtEnd);
    template <class PARAM>
    Pair_Second(const PARAM&                   value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAllocatorArgT);
    template <class PARAM>
    Pair_Second(PARAM&                         value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomAllocatorArgT);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `second` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ...ARGS, size_t ...I>
    Pair_Second(std::tuple<ARGS...>&& argsPack,
                BloombergLP::bslstl::Pair_IndexSequence<I...>);
#endif
};

/// This component-private `class` holds the `second` data member of a `pair`
/// when that member is an lvalue-reference.  Note that the C++ Standard
/// defines the assignment operator to assign through the reference in this
/// case, where the implictly declared assignment operator would be deleted.
template <class TYPE>
struct Pair_Second<TYPE&> {

  protected:
    // PROTECTED DATA
    TYPE& second;

    // CREATORS

    /// Bind to `second` the entity of (template parameter) `TYPE` that is
    /// bound to the `second` reference of `other`.  Note that, unlike the
    /// primary template, these constructors must be declared and defaulted
    /// as the user-declared copy-assignment operator would inhibit the
    /// declaration of the move constructor, which in turn would mean that
    /// the implicitly declared constructors for the derived `pair` class
    /// would be just the copy constructor taking a `const` reference, even
    /// when the second element is move-only.  Declaring the move constructor
    /// deletes the copy constructor unless it too is declared and defaulted.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    Pair_Second(const Pair_Second& other) = default;
    Pair_Second(Pair_Second&& other) = default;
#endif

    /// Bind the specified `value` into the `second` reference-member.
    BSLS_KEYWORD_CONSTEXPR
    explicit Pair_Second(TYPE& value);

    /// Bind the specified 'value' into the 'second' reference-member.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM, class = bsl::enable_if_t<
                                   std::is_constructible<TYPE, PARAM>::value>>
    constexpr
    explicit Pair_Second(PARAM&& value);
#else   // no C++11 support
    template <class PARAM>
    explicit Pair_Second(const PARAM& value);   // for derived-to-const-base-ref
    template <class PARAM>
    explicit Pair_Second(PARAM& value);
#endif

    /// Bind the specified 'value' into the 'second' reference-member.  The
    /// specified 'basicAllocator' is not used.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM>
    constexpr
    Pair_Second(PARAM&&                        value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
#else   // no C++11 support
    template <class PARAM>
    Pair_Second(const PARAM&                   value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
    template <class PARAM>
    Pair_Second(PARAM&                         value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `second` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ARG>
    constexpr
    Pair_Second(std::tuple<ARG>&& arg,
                BloombergLP::bslstl::Pair_IndexSequence<0u>);
#endif

    // MANIPULATORS

    /// Assign to the object bound to `second` the value of the object bound
    /// to 'rhs.second', and return a reference offering modifiable access to
    /// this object.  Note that this operations assigns through the reference,
    /// it does not rebind the reference.
    Pair_Second& operator=(const Pair_Second& rhs) BSLS_KEYWORD_NOEXCEPT;
};

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
/// This component-private `class` holds the `second` data member of a `pair`
/// when that member is an rvalue-reference.  Note that the C++ Standard
/// defines the assignment operator to assign through the reference in this
/// case, where the implictly declared assignment operator would be deleted.
template <class TYPE>
struct Pair_Second<TYPE&&> {

  protected:
    // PROTECTED DATA
    TYPE&& second;

    // CREATORS

    /// Bind to `second` the entity of (template parameter) `TYPE` that is
    /// bound to the `second` reference of `other`.  Note that, unlike the
    /// primary template, these constructors must be declared and defaulted
    /// as the user-declared copy-assignment operator would inhibit the
    /// declaration of the move constructor, which in turn would mean that
    /// the implicitly declared constructors for the derived `pair` class
    /// would be just the copy constructor taking a `const` reference, even
    /// when the second element is move-only.  Declaring the move constructor
    /// deletes the copy constructor unless it too is declared and defaulted.
    Pair_Second(const Pair_Second&) = default;
    Pair_Second(Pair_Second&&) = default;

    /// Bind the specified `value` into the `second` reference-member.
    constexpr
    explicit Pair_Second(TYPE&& value);

    /// Bind the specified 'value' into the 'second' reference-member.
    template <class PARAM, class = bsl::enable_if_t<
                                   std::is_constructible<TYPE, PARAM>::value>>
    constexpr
    explicit Pair_Second(PARAM&& value);

    /// Bind the specified 'value' into the 'second' reference-member.  The
    /// specified 'basicAllocator' is not used.
    template <class PARAM>
    constexpr
    Pair_Second(PARAM&&                        value,
                BloombergLP::bslma::Allocator *basicAllocator,
                Pair_BslmaIdiomNone);

# if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Construct the `second` member of a `pair`, forwarding in order the
    /// elements in the specified `argsPack` to the corresponding
    /// constructor of (template parameter) `TYPE`.  The length of the
    /// `argsPack` is equal to the lenght of the specified (template
    /// parameter pack) `I...` and passed to the constructor via the
    /// `Pair_IndexSequence` object.
    template <class ARG>
    constexpr
    Pair_Second(std::tuple<ARG>&& arg,
                BloombergLP::bslstl::Pair_IndexSequence<0u>);
# endif

    // MANIPULATORS

    /// Assign to the object bound to `second` the value of the object bound
    /// to 'rhs.second', and return a reference offering modifiable access to
    /// this object.  Note that this operations assigns through the reference,
    /// it does not rebind the reference.
    Pair_Second& operator=(const Pair_Second& rhs) noexcept;
};
#endif

                                // ==========
                                // class pair
                                // ==========

/// The class template `pair` provides a pair of public data members,
/// `first` and `second`, of type `T1` and `T2` respectively.  If either
/// `T1` or `T2` uses `bslma::Allocator` for memory management, then provide
/// an optional `bslma::Allocator` argument for each constructor, to be
/// passed through to the constructors of `first` and/or `second` as
/// appropriate.  The interface to this class is identical to the standard
/// `std::pair` except for the addition of the allocators.  Note that the
/// implementation of this class provides `first` and `second` through
/// multiple base classes in order to simplify construction of each member
/// when allowing for the various rules for passing allocators in C++11.
template <class T1, class T2>
class pair : public Pair_First<T1>, public Pair_Second<T2> {

    // PRIVATE TYPES
    typedef Pair_First<T1>                                 FirstBase;

    /// `ThirdBase` and `HomePlate` left as an exercise for the reader.
    typedef Pair_Second<T2>                                SecondBase;

    typedef typename Pair_BslmaIdiom<T1>::type             FirstBslmaIdiom;
    typedef typename Pair_BslmaIdiom<T2>::type             SecondBslmaIdiom;

    /// This typedef is a convenient alias for the utility associated with
    /// movable references.
    typedef BloombergLP::bslmf::MovableRefUtil             MovUtil;

    /// This empty `struct` is used with `bsl::enable_if` in template
    /// constraints.
    struct SfinaeEnable {};

  public:
    // PUBLIC TYPES
    typedef T1 first_type;
    typedef T2 second_type;

    // PUBLIC DATA
    using FirstBase::first;
    using SecondBase::second;

    // CREATORS

    /// Construct a `pair` with the `first` and `second` members initialized
    /// to default values.  Optionally specify a `basicAllocator`, used to
    /// supply memory for each of `first` and `second` when its type
    /// (template parameter `T1` or `T2`, respectively) uses `bslma`-style
    /// allocators.  This method requires that `T1` and `T2` be
    /// default-constructible.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    constexpr pair() = default;
#else
    pair();
#endif
    explicit pair(BloombergLP::bslma::Allocator *basicAllocator);

    /// The copy constructor is declared implicitly, and will be implcitly
    /// deleted if either `T1` or `T2` have inaccessible or deleted copy
    /// constructors.  If either (template) type parameters, `T1` or `T2`, has
    /// a copy constructor that is declared to take `TYPE&` rather than
    /// `const TYPE &` then this constructor will be declared with a `pair &`
    /// parameter; otherwise this constructor will be declared with a
    /// `const pair &` argument.  This constructor is trivial if both
    /// (template) type parameters `T1` and `T2` have trivial copy
    /// constructors.
    //! pair(const pair&) = default;

    /// Construct a `pair` having the same value as that of the specified
    /// `original` pair.  Optionally specify a `basicAllocator`, used to
    /// supply memory for each of `first` and `second` when its type
    /// (template parameter `T1` or `T2`, respectively) uses `bslma`-style
    /// allocators.  Note that the copy constructor is implicitly declared
    /// (if `T1` and `T2` are both `copy-constructible`) by compilers that
    /// do not support defaulted declarations.
    pair(const pair& original, BloombergLP::bslma::Allocator *basicAllocator);

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// The move constructor is declared implicitly, and will be implcitly
    /// deleted if move constructor for either `T1` or `T2` is inaccessible or
    /// deleted.  This constructor is trivial if both (template) type
    /// parameters `T1` and `T2` are trivially move constructible.
    //! pair(pair&& original) = default;

    /// Construct a pair having the same value as that of the specified
    /// `original` using the specified `basicAllocator` to supply memory for
    /// each of `first` and `second` when its type (template parameter `T1`
    /// or `T2`, respectively) uses `bslma`-style allocators.  Note that
    /// `original` is left in a valid but unspecified state.  Also note that
    /// this method requires that `T1` and `T2` be move-constructible.
    /// Note this this constructor will *not* be deleted when the implicitly
    /// declared move constructor is deleted, and will produce an error when
    /// it is called in such cases.
    pair(pair&& original, BloombergLP::bslma::Allocator *basicAllocator);
#else   // no C++11 support
    /// Construct a pair having the same value as that of the specified
    /// `original` before the call to the move constructor.  Optionally
    /// specify a `basicAllocator`, used to supply memory for each of
    /// `first` and `second` when its type (template parameter `T1` or `T2`,
    /// respectively) uses `bslma`-style allocators.  Note that `original`
    /// is left in a valid but unspecified state.  Also note that this
    /// method requires that `T1` and `T2` be move-constructible.
    pair(BloombergLP::bslmf::MovableRef<pair>  original);
    pair(BloombergLP::bslmf::MovableRef<pair>  original,
         BloombergLP::bslma::Allocator        *basicAllocator);
#endif

    /// Construct a `pair` with the `first` member initialized to the
    /// specified `a` value and the `second` member initialized to the
    /// specified `b` value.  Optionally specify a `basicAllocator`, used to
    /// supply memory for each of `first` and `second` when its type
    /// (template parameter `T1` or `T2`, respectively) uses `bslma`-style
    /// allocators.  This method requires that `T1` and `T2` be
    /// `copy-constructible`.
    BSLS_KEYWORD_CONSTEXPR
    pair(typename bsl::add_lvalue_reference<const T1>::type  a,
         typename bsl::add_lvalue_reference<const T2>::type  b);
    pair(typename bsl::add_lvalue_reference<const T1>::type  a,
         typename bsl::add_lvalue_reference<const T2>::type  b,
         BloombergLP::bslma::Allocator                      *basicAllocator);

    /// Construct a pair with the 'first' member initialized to the specified
    /// 'a' value of (template parameter) type 'PARAM_1' and the 'second' member
    /// initialized to the specified 'b' value of (template parameter) type
    /// 'PARAM_2'.  Optionally specify a 'basicAllocator', used to supply memory
    /// for each of 'first' and 'second' when its type (template parameter 'T1'
    /// or 'T2', respectively) uses 'bslma'-style allocators.  This method
    /// requires that 'T1' and 'T2' be convertible from 'PARAM_1' and 'PARAM_2',
    /// respectively.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM_1,
              class PARAM_2,
              class = bsl::enable_if_t<
                std::is_constructible<T1, PARAM_1>::value &&
                std::is_constructible<T2, PARAM_2>::value &&
                !(bsl::is_pointer<bsl::remove_reference_t<PARAM_2>>::value &&
                  bsl::is_convertible<PARAM_2,
                                      BloombergLP::bslma::Allocator *>::value)>
             >
    constexpr pair(PARAM_1&& a, PARAM_2&& b);
    template <class PARAM_1, class PARAM_2>
    pair(PARAM_1&&                      a,
         PARAM_2&&                      b,
         BloombergLP::bslma::Allocator *basicAllocator);
#else   // no C++11 support
    template <class PARAM_1, class PARAM_2>
    pair(const PARAM_1& a,
         const PARAM_2& b,
         typename bsl::enable_if<
            bsl::is_convertible<PARAM_1, T1>::value &&
            bsl::is_convertible<PARAM_2, T2>::value &&
                !(bsl::is_pointer<
                      typename bsl::remove_reference<PARAM_2>::type>::value &&
                  bsl::is_convertible<PARAM_2,
                                      BloombergLP::bslma::Allocator *>::value),
            SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(a)
    , SecondBase(b)
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (MSVC 2010) bug where the definition
        // cannot be matched to the declaration when an 'enable_if' is used.
    }
    template <class PARAM_1, class PARAM_2>
    pair(const PARAM_1&                 a,
         const PARAM_2&                 b,
         BloombergLP::bslma::Allocator *basicAllocator);

    template <class PARAM_1, class PARAM_2>
    pair(PARAM_1&       a,
         const PARAM_2& b,
         typename bsl::enable_if<
            bsl::is_convertible<PARAM_1, T1>::value &&
            bsl::is_convertible<PARAM_2, T2>::value &&
                !(bsl::is_pointer<
                      typename bsl::remove_reference<PARAM_2>::type>::value &&
                  bsl::is_convertible<PARAM_2,
                                      BloombergLP::bslma::Allocator *>::value),
            SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(a)
    , SecondBase(b)
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (MSVC 2010) bug where the definition
        // cannot be matched to the declaration when an 'enable_if' is used.
    }
    template <class PARAM_1, class PARAM_2>
    pair(PARAM_1&                       a,
         const PARAM_2&                 b,
         BloombergLP::bslma::Allocator *basicAllocator);

    template <class PARAM_1, class PARAM_2>
    pair(const PARAM_1& a,
         PARAM_2&       b,
         typename bsl::enable_if<
            bsl::is_convertible<PARAM_1, T1>::value &&
            bsl::is_convertible<PARAM_2, T2>::value &&
                !(bsl::is_pointer<
                      typename bsl::remove_reference<PARAM_2>::type>::value &&
                  bsl::is_convertible<PARAM_2,
                                      BloombergLP::bslma::Allocator *>::value),
            SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(a)
    , SecondBase(b)
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (MSVC 2010) bug where the definition
        // cannot be matched to the declaration when an 'enable_if' is used.
    }
    template <class PARAM_1, class PARAM_2>
    pair(const PARAM_1&                 a,
         PARAM_2&                       b,
         BloombergLP::bslma::Allocator *basicAllocator);

    template <class PARAM_1, class PARAM_2>
    pair(PARAM_1& a,
         PARAM_2& b,
         typename bsl::enable_if<
            bsl::is_convertible<PARAM_1, T1>::value &&
            bsl::is_convertible<PARAM_2, T2>::value &&
                !(bsl::is_pointer<
                      typename bsl::remove_reference<PARAM_2>::type>::value &&
                  bsl::is_convertible<PARAM_2,
                                      BloombergLP::bslma::Allocator *>::value),
            SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(a)
    , SecondBase(b)
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (MSVC 2010) bug where the definition
        // cannot be matched to the declaration when an 'enable_if' is used.
    }
    template <class PARAM_1, class PARAM_2>
    pair(PARAM_1&                       a,
         PARAM_2&                       b,
         BloombergLP::bslma::Allocator *basicAllocator);
#endif

    /// Construct a `pair` from the specified `other` pair, holding `first`
    /// and `second` values of (template parameter) type `PARAM_1` and
    /// `PARAM_2` respectively.  Optionally specify a `basicAllocator`, used
    /// to supply memory for each of `first` and `second` when its type
    /// (template parameter `T1` or `T2`, respectively) uses `bslma`-style
    /// allocators.  This method requires that `T1` and `T2` be convertible
    /// from `PARAM_1` and `PARAM_2`, respectively.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<
                            std::is_constructible<T1, const PARAM_1&>::value &&
                            std::is_constructible<T2, const PARAM_2&>::value>>
    constexpr pair(const pair<PARAM_1, PARAM_2>& other);
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<
                            std::is_constructible<T1, const PARAM_1&>::value &&
                            std::is_constructible<T2, const PARAM_2&>::value>>
    constexpr pair(const std::pair<PARAM_1, PARAM_2>& other);
#else   // no C++11 support
    template <class PARAM_1, class PARAM_2>
    pair(const pair<PARAM_1, PARAM_2>& other);
    template <class PARAM_1, class PARAM_2>
    pair(const std::pair<PARAM_1, PARAM_2>& other);
#endif
    template <class PARAM_1, class PARAM_2>
    pair(const pair<PARAM_1, PARAM_2>&       other,
         BloombergLP::bslma::Allocator      *basicAllocator);
    template <class PARAM_1, class PARAM_2>
    pair(const std::pair<PARAM_1, PARAM_2>&  other,
         BloombergLP::bslma::Allocator      *basicAllocator);

    /// Construct a `pair` from the specified `other` pair, holding `first`
    /// and `second` values of (template parameter) type `PARAM_1` and
    /// `PARAM_2` respectively.  Optionally specify a `basicAllocator`, used
    /// to supply memory for each of `first` and `second` when its type
    /// (template parameter `T1` or `T2`, respectively) uses `bslma`-style
    /// allocators.  This method requires that `T1` and `T2` be convertible
    /// from `PARAM_1` and `PARAM_2`, respectively.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<bsl::is_convertible<PARAM_1, T1>::value
                                    && bsl::is_convertible<PARAM_2, T2>::value>
             >
    constexpr pair(pair<PARAM_1, PARAM_2>&& other);
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<bsl::is_convertible<PARAM_1, T1>::value
                                    && bsl::is_convertible<PARAM_2, T2>::value>
             >
    constexpr pair(std::pair<PARAM_1, PARAM_2>&& other);
    template <class PARAM_1, class PARAM_2>
    pair(pair<PARAM_1, PARAM_2>&&       other,
         BloombergLP::bslma::Allocator *basicAllocator);
    template <class PARAM_1, class PARAM_2>
    pair(std::pair<PARAM_1, PARAM_2>&&  other,
         BloombergLP::bslma::Allocator *basicAllocator);
#else   // no C++11 support
    template <class PARAM_1, class PARAM_2>
    pair(BloombergLP::bslmf::MovableRef<pair<PARAM_1, PARAM_2> > other,
         typename bsl::enable_if<bsl::is_convertible<PARAM_1, T1>::value &&
                                 bsl::is_convertible<PARAM_2, T2>::value,
                                 SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(MovUtil::move(MovUtil::access(other).first))
    , SecondBase(MovUtil::move(MovUtil::access(other).second))
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (version 16) bug where the
        // definition cannot be matched to the declaration when an 'enable_if'
        // is used.
    }

    template <class PARAM_1, class PARAM_2>
    pair(BloombergLP::bslmf::MovableRef<std::pair<PARAM_1, PARAM_2> > other,
         typename bsl::enable_if<bsl::is_convertible<PARAM_1, T1>::value &&
                                 bsl::is_convertible<PARAM_2, T2>::value,
                                 SfinaeEnable>::type = SfinaeEnable())
    : FirstBase(MovUtil::move(MovUtil::access(other).first))
    , SecondBase(MovUtil::move(MovUtil::access(other).second))
    {
        // The implementation is placed here in the class definition to work
        // around a Microsoft C++ compiler (version 16) bug where the
        // definition cannot be matched to the declaration when an 'enable_if'
        // is used.
    }
    template <class PARAM_1, class PARAM_2>
    pair(
      BloombergLP::bslmf::MovableRef<pair<PARAM_1, PARAM_2> >  other,
      BloombergLP::bslma::Allocator                           *basicAllocator);
    template <class PARAM_1, class PARAM_2>
    pair(BloombergLP::bslmf::MovableRef<std::pair<PARAM_1, PARAM_2> > other,
         BloombergLP::bslma::Allocator *basicAllocator);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Create a `pair` from piece-wise construction of `first` and `second`
    /// values by forwarding in order the elements in the specified
    /// `first_args` and `second_args` tuples to the corresponding
    /// constructor of (template parameter) types `T1` and `T2`,
    /// respectively.  Optionally specify a `basicAllocator`, used to supply
    /// memory for each of `first` and `second` when its type (template
    /// parameter `T1` or `T2`, respectively) uses `bslma`-style allocators.
    /// Allocators can also be passed as tuple members straight to `T1` or
    /// `T2` (or both) constructors using the first version (but use of the
    /// second version for this approach will result in a compile-time
    /// error).  This method requires that `T1` and `T2` be constructible
    /// from (the variable number of template parameters) `ARGS_1` and
    /// `ARGS_2` respectively.
    template<class ...ARGS_1, class ...ARGS_2>
    pair(std::piecewise_construct_t,
         std::tuple<ARGS_1...>          first_args,
         std::tuple<ARGS_2...>          second_args);
    template<class ...ARGS_1, class ...ARGS_2>
    pair(std::piecewise_construct_t,
         std::tuple<ARGS_1...>          first_args,
         std::tuple<ARGS_2...>          second_args,
         BloombergLP::bslma::Allocator *basicAllocator);
#endif

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
    /// Create a `pair` that has the same value as the specified `rhs` pair
    /// proxy.  The behavior is undefined unless `T1` is constructible from
    /// `PARAM_1` and `T2` is constructible from from `PARAM_2`.
    template <class PARAM_1, class PARAM_2>
    BSLS_KEYWORD_CONSTEXPR
    pair(const BloombergLP::bslma::ManagedPtr_PairProxy<PARAM_1, PARAM_2>&
                                                              rhs); // IMPLICIT
#endif // BDE_OMIT_INTERNAL_DEPRECATED

    // Destroy this object.  Call destructors on 'first' and 'second'.
    // This destructor is trivial if both `T1` and `T2` have trivial
    /// destructors.
    //! ~pair() = default;

    // MANIPULATORS

    /// The copy and move assignment operators are implicitly declared and
    /// defined.  They are trivial is the corresponding assignment operators
    /// are trivial for both (template parameter) types `T1` and `T2`.
    //! pair& operator=(const pair& rhs) = default;

    /// Assign to this `pair` from the specified `rhs` pair, holding the
    /// parameterized types `PARAM_1` and `PARAM_2`, and return a reference
    /// offering modifiable access to this object.  Assign `first` the value
    /// in `rhs.first` and `second` the value in `rhs.second`.  Attempted
    /// use of this assignment operator will not compile unless both `T1`
    /// and `T2` supply assignment operators, and `T1` is assignable from
    /// `PARAM_1` and `T2` is assignable from `PARAM_2`.
#if defined(BSLS_COMPILERFEATURES_FULL_CPP14)
    // The SFINAE constraint requires C++14 as it create ambiguities in C++11
    // and earlier.
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<std::is_assignable<T1&, const PARAM_1>::value
                                    && std::is_assignable<T2&, const PARAM_2>::value>
             >
    constexpr
    pair& operator=(const pair<PARAM_1, PARAM_2>& rhs);
#else   // no C++14 support
    template <class PARAM_1, class PARAM_2>
    pair& operator=(const pair<PARAM_1, PARAM_2>& rhs);
#endif

    /// Assign to this `pair` from the specified `rhs` pair, where the type
    /// `rhs` is the pair type native to the compiler's library, holding the
    /// parameterized types `PARAM_1` and `PARAM_2`, and return a reference
    /// offering modifiable access to this object.  Assign `first` the value
    /// in `rhs.first` and `second` the value in `rhs.second`.  Attempted
    /// use of this assignment operator will not compile unless both `T1`
    /// and `T2` supply assignment operators, and `T1` is assignable from
    /// `PARAM_1` and `T2` is assignable from `PARAM_2`.
    template <class PARAM_1, class PARAM_2>
    BSLS_KEYWORD_CONSTEXPR_CPP14
    pair& operator=(const std::pair<PARAM_1, PARAM_2>& rhs);

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// This move assignment operator is implicitly declared and defined.
    /// It has a non-throwing exception specification if the move assignment
    /// operators for both (template parameter) types `T1` and `T2` have
    /// non-throwing exception specifications.  It is trivial is the move
    /// assignment operators for both (template parameter) types `T1` and `T2`
    /// are trivial.
    //! pair& operator=(pair&& rhs) = default;

    /// Assign to this `pair` the value of the specified `rhs` pair, holding
    /// `first` and `second` values of (template parameter) type `PARAM_1`
    /// and `PARAM_2` respectively, and return a reference providing
    /// modifiable access to this object.  This method requires that `T1` be
    /// assignable from `PARAM_1` and `T2` be assignable from `PARAM_2`.
    template <class PARAM_1, class PARAM_2>
    BSLS_KEYWORD_CONSTEXPR_CPP14
    pair& operator=(pair<PARAM_1, PARAM_2>&& rhs);
#else   // no C++11 support
    /// Assign to this 'pair' the value of the specified 'rhs' pair (before
    /// the call to the assignment), and return a reference providing
    /// modifiable access to this object.  Note that 'rhs' is left in a
    /// valid but unspecified state.  This method requires that (template
    /// parameter) types 'T1' and 'T2' be move-assignable.
    pair& operator=(BloombergLP::bslmf::MovableRef<pair> rhs);

    /// Assign to this `pair` the value of the specified `rhs` pair, holding
    /// `first` and `second` values of (template parameter) types `PARAM_1`
    /// and `PARAM_2` respectively, and return a reference providing
    /// modifiable access to this object.  This method requires that `T1` be
    /// assignable from `PARAM_1` and `T2` be assignable from `PARAM_2`.
    template <class PARAM_1, class PARAM_2>
    pair& operator=(
                  BloombergLP::bslmf::MovableRef<pair<PARAM_1, PARAM_2> > rhs);
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
    /// Return an `std::tuple` object, holding references that provide
    /// modifiable access to the members of this object.
    template <class PARAM_1, class PARAM_2,
              class = bsl::enable_if_t<bsl::is_convertible<T1, PARAM_1>::value
                                    && bsl::is_convertible<T2, PARAM_2>::value>
             >
    operator std::tuple<PARAM_1&, PARAM_2&>() noexcept;

    /// This overload of `template <class PARAM_1, class PARAM_2>
    /// operator std::tuple<PARAM_1&, PARAM_2&>()` is called when the (template
    /// parameter) `PARAM_2` (second element's type) is the type of
    /// `std::ignore`.
    template <class PARAM_1,
              class = bsl::enable_if_t<bsl::is_convertible<T1, PARAM_1>::value>
             >
    operator std::tuple<PARAM_1&, decltype(std::ignore)&>() noexcept;

    /// This overload of `template <class PARAM_1, class PARAM_2>
    /// operator std::tuple<PARAM_1&, PARAM_2&>()` is called when the (template
    /// parameter) `PARAM_1` (first element's type) is the type of
    /// `std::ignore`.
    template <class PARAM_2,
              class = bsl::enable_if_t<bsl::is_convertible<T2, PARAM_2>::value>
             >
    operator std::tuple<decltype(std::ignore)&, PARAM_2&>() noexcept;

    /// This overload of `template <class PARAM_1, class PARAM_2>
    /// operator std::tuple<PARAM_1&, PARAM_2&>()` is called when the (template
    /// parameters) `PARAM_1` (first element's type) and `PARAM_2` (second
    /// element's type) are the type of `std::ignore`.  Note that this method is
    /// defined within the class body intentionally to avoid build failure on
    /// MSVC 2015.
    operator std::tuple<decltype(std::ignore)&,
                        decltype(std::ignore)&>() noexcept
    {
        return std::tuple<decltype(std::ignore)&,
                          decltype(std::ignore)&>(std::ignore,
                                                  std::ignore);
    }
#endif

    /// Swap the value of this pair with the value of the specified 'other'
    /// pair by applying 'swap' to each of the 'first' and 'second' pair
    /// fields.  Note that this method is no-throw only if 'swap' on each
    /// field is no-throw.
#if defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
    void swap(pair& other) BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                       Pair_ImpUtil::hasNothrowSwap<T1, T2>());
#else   // has full C++11 support
    void swap(pair& other) noexcept(bsl::is_nothrow_swappable<T1>::value &&
                                    bsl::is_nothrow_swappable<T2>::value);
#endif
};

#ifdef BSLS_COMPILERFEATURES_SUPPORT_CTAD
// CLASS TEMPLATE DEDUCTION GUIDES

// When we are deducing from a 'pair<T1, T2>' (std or bsl) and an
// 'Allocator *', we want to deduce a 'bsl::pair<T1, T2>', not a
// bsl::pair<pair<T1, T2>, Allocator *>.
//
// Note that order is important; if given an 'Allocator *' and a
// 'pair<T1, T2>' we deduce 'bsl::pair<Allocator *, pair<T1, T2>>'.

/// Deduce the specified types `T1` and `T2` from the corresponding types
/// supplied to the constructor of `pair`.
template <class T1, class T2>
pair(T1, T2) -> pair<T1, T2>;

/// Deduce the specified types `T1` and `T2` from the corresponding types
/// supplied to the constructor of `pair`.  This guide does not participate
/// in deduction unless the specified `ALLOC` inherits from
/// `bslma::Allocator`.
template <
    class T1,
    class T2,
    class ALLOC,
    class = bsl::enable_if_t<
         bsl::is_convertible_v<ALLOC *, BloombergLP::bslma::Allocator *>>
    >
pair(T1, T2, ALLOC *) -> pair<T1, T2>;

/// Deduce the specified types `T1` and `T2` from the corresponding template
/// parameters of the `bsl::pair` supplied to the constructor of `pair`.
/// This guide does not participate in deduction unless the specified
/// `ALLOC` inherits from `bslma::Allocator`.
template <
    class T1,
    class T2,
    class ALLOC,
    class = bsl::enable_if_t<
         bsl::is_convertible_v<ALLOC *, BloombergLP::bslma::Allocator *>>
    >
pair(pair<T1, T2>, ALLOC *) -> pair<T1, T2>;

/// Deduce the specified types `T1` and `T2` from the corresponding template
/// parameters of the `std::pair` supplied to the constructor of `pair`.
template <class T1, class T2>
pair(std::pair<T1, T2>) -> pair<T1, T2>;

/// Deduce the specified types `T1` and `T2` from the corresponding template
/// parameters of the `std::pair` supplied to the constructor of `pair`.
/// This guide does not participate in deduction unless the specified
/// `ALLOC` inherits from `bslma::Allocator`.
template <
    class T1,
    class T2,
    class ALLOC,
    class = bsl::enable_if_t<
         bsl::is_convertible_v<ALLOC *, BloombergLP::bslma::Allocator *>>
    >
pair(std::pair<T1, T2>, ALLOC *) -> pair<T1, T2>;
#endif

// FREE OPERATORS

/// Return true if the specified `lhs` and `rhs` pair objects have the same
/// value and false otherwise.  `lhs` has the same value as `rhs` if
/// `lhs.first == rhs.first` and `lhs.second == rhs.second`.  A call to this
/// operator will not compile unless both `T1` and `T2` supply `operator==`.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator==(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

#if !defined(BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON)
/// Return true if the specified `lhs` and `rhs` pair objects do not have
/// the same value and false otherwise.  `lhs` does not have the same value
/// as `rhs` if `lhs == rhs` would return false.  A call to this operator
/// will not compile unless a call to `lhs == rhs` would compile.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);
#endif

#if defined(BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE)

/// Perform a lexicographic three-way comparison of the specified `lhs` and
/// the specified `rhs` pairs by using the comparison operators of `T1` and
/// `T2`; return the result of that comparison.
template <class T1, class T2>
constexpr
std::common_comparison_category_t<
    BloombergLP::bslalg::SynthThreeWayUtil::Result<T1>,
    BloombergLP::bslalg::SynthThreeWayUtil::Result<T2>
> operator<=>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

#else

/// Return true if the specified `lhs` has a value less than the specified
/// `rhs` and false otherwise.  Whether or not `lhs` is less than `rhs` is
/// determined by a lexicographical comparison of the `first` and `second`
/// data members of `lhs` and `rhs`.  In other words: return true if
/// `lhs.first < rhs.first` and false if `rhs.first < lhs.first`, otherwise
/// return `lhs.second < rhs.second`.  A call to this operator will not
/// compile unless both `T1` and `T2` supply `operator<`.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator<(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

/// Return true if the specified `lhs` has a value greater than the
/// specified `rhs` and false otherwise.  `lhs` has a value greater than
/// `rhs` if `rhs` < `lhs` would return true.  A call to this operator will
/// not compile unless a call to `lhs < rhs` would compile.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

/// Return true if the specified `lhs` has a value less than or equal to the
/// specified `rhs` and false otherwise.  `lhs` has a value less than or
/// equal to `rhs` if `rhs` < `lhs` would return false.  A call to this
/// operator will not compile unless a call to `lhs < rhs` would compile.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

/// Return true if the specified `lhs` has a value greater than or equal to
/// the specified `rhs` and false otherwise.  `lhs` has a value greater than
/// or equal to `rhs` if `lhs` < `rhs` would return false.  A call to this
/// operator will not compile unless a call to `lhs < rhs` would compile.
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);

#endif  // BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

// FREE FUNCTIONS

/// Swap the values of the specified `a` and `b` pairs by applying `swap` to
/// each of the `first` and `second` pair fields.  Note that this method is
/// no-throw only if `swap` on each field is no-throw.
template <class T1, class T2>
#if defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
void swap(pair<T1, T2>& a, pair<T1, T2>& b)
                      BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(noexcept(a.swap(b)));
#else   // has full C++11 support
bsl::enable_if_t<bsl::is_swappable<T1>::value && bsl::is_swappable<T2>::value>
swap(pair<T1, T2>& a, pair<T1, T2>& b) noexcept(noexcept(a.swap(b)));
#endif

// HASH SPECIALIZATIONS

/// Pass the specified `input` to the specified `hashAlg`
template <class HASHALG, class T1, class T2>
void hashAppend(HASHALG& hashAlg, const pair<T1, T2>&  input);

}  // close namespace bsl

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)

namespace std {

# if defined(BSLS_PLATFORM_CMP_CLANG)
#   pragma clang diagnostic push
#   pragma clang diagnostic ignored "-Wmismatched-tags"
# endif

                             // ====================
                             // struct tuple_element
                             // ====================

/// This partial specialization of `tuple_element` provides compile-time
/// access to the type of the pair's first element.
template<class T1, class T2>
struct tuple_element<0, bsl::pair<T1, T2> >
{
    // TYPES
    using type = T1;
};

/// This partial specialization of `tuple_element` provides compile-time
/// access to the type of the pair's second element.
template<class T1, class T2>
struct tuple_element<1, bsl::pair<T1, T2> >
{
    // TYPES
    using type = T2;
};

                              // =================
                              // struct tuple_size
                              // =================

/// This meta-function provides a compile-time way to obtain the number of
/// elements in a pair, which is always 2.
template<class T1, class T2>
struct tuple_size<bsl::pair<T1, T2> > : integral_constant<size_t, 2>
{};

# if defined(BSLS_PLATFORM_CMP_CLANG)
#   pragma clang diagnostic pop
# endif

}  // close namespace std

namespace BloombergLP {
namespace bslstl {

                          // =====================
                          // class Pair_GetImpUtil
                          // =====================

/// This utility class template provides functions for selecting the element
/// of pair, returned from `bsl::get(bsl::pair<T1, T2>)`, by its index.
template <std::size_t INDEX, class T1, class T2>
struct Pair_GetImpUtil
{
    static_assert(INDEX < 2, "Element index does not exist");
};

/// This partial specialization of `Pair_GetImpUtil`, for when the
/// (template parameter) `INDEX`(element's index) is equal to 0.
template <class T1, class T2>
struct Pair_GetImpUtil<0, T1, T2>
{
    // CLASS METHODS

    /// Return a reference providing modifiable access to the first element
    /// of the specified `p`.
    static T1& getPairElement(bsl::pair<T1, T2>& p) noexcept;

    /// Return a reference providing non-modifiable access to the first
    /// element of the specified `p`.
    static
    const T1& getPairElement(const bsl::pair<T1, T2>& p) noexcept;

    /// Return a rvalue reference providing modifiable access to the first
    /// element of the specified `p`
    static T1&& getPairElement(bsl::pair<T1, T2>&& p) noexcept;

    /// Return a rvalue reference providing non-modifiable access to the
    /// first element of the specified `p`
    static const T1&& getPairElement(const bsl::pair<T1, T2>&& p) noexcept;
};

/// This partial specialization of `Pair_GetImpUtil`, for when the
/// (template parameter) `INDEX`(element's index) is equal to 1.
template <class T1, class T2>
struct Pair_GetImpUtil<1u, T1, T2>
{
    // CLASS METHODS

    /// Return a reference providing modifiable access to the second element
    /// of the specified `p`.
    static T2& getPairElement(bsl::pair<T1, T2>& p) noexcept;

    /// Return a reference providing non-modifiable access to the second
    /// element of the specified `p`.
    static const T2& getPairElement(const bsl::pair<T1, T2>& p) noexcept;

    /// Return a rvalue reference providing modifiable access to the second
    /// element of the specified `p`
    static T2&& getPairElement(bsl::pair<T1, T2>&& p) noexcept;

    /// Return a rvalue reference providing non-modifiable access to the
    /// second element of the specified `p`
    static const T2&& getPairElement(const bsl::pair<T1, T2>&& p) noexcept;
};

/// This meta-function provides a compile-time way to obtain the index of
/// `bsl::pair` element, having the (template parameter) type `T`.  If
/// neither type of pair's element is equal to `T` or both pair's elements
/// have the same type, code is not compiled.
template <class T, class T1, class T2>
struct Pair_IndexOfType
{};

/// This partial specialization of `Pair_IndexOfType` returns the index of
/// first element of pair.
template <class T1, class T2>
struct Pair_IndexOfType<T1, T1, T2> : bsl::integral_constant<size_t, 0>
{};

/// This partial specialization of `Pair_IndexOfType` returns the index of
/// second element of pair.
template <class T1, class T2>
struct Pair_IndexOfType<T2, T1, T2> : bsl::integral_constant<size_t, 1u>
{};

}  // close package namespace
}  // close enterprise namespace

namespace bsl {

// FREE FUNCTIONS

/// Return a reference providing modifiable access to the element of the
/// specified `p`, having the ordinal number specified by the (template
/// parameter) `INDEX`.  This function will not compile unless the `INDEX`
/// is either 0 or 1.
template<std::size_t INDEX, class T1, class T2>
typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&
get(bsl::pair<T1, T2>& p) noexcept;

/// Return a reference providing non-modifiable access to the element of the
/// specified `p`, having the ordinal number specified by the (template
/// parameter) `INDEX`.  This function will not compile unless the `INDEX`
/// is either 0 or 1.
template<std::size_t INDEX, class T1, class T2>
const typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&
get(const bsl::pair<T1, T2>& p) noexcept;

/// Return a rvalue reference providing modifiable access to the element of
/// the specified `p`, having the ordinal number specified by the (template
/// parameter) `INDEX`.  This function will not compile unless the `INDEX`
/// is either 0 or 1.
template<std::size_t INDEX, class T1, class T2>
typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&&
get(bsl::pair<T1, T2>&& p) noexcept;

/// Return a reference providing modifiable access to the element of the
/// specified `p`, having the (template parameter) `TYPE`.  This function
/// will not compile unless the types `T1` and `T2` are different and the
/// `TYPE` is the same as either `T1` or `T2`.
template<class TYPE, class T1, class T2>
TYPE& get(bsl::pair<T1, T2>& p) noexcept;

/// Return a reference providing non-modifiable access to the element of the
/// specified `p`, having the (template parameter) `TYPE`.  This function
/// will not compile unless the types `T1` and `T2` are different and the
/// `TYPE` is the same as either `T1` or `T2`.
template<class TYPE, class T1, class T2>
const TYPE& get(const bsl::pair<T1, T2>& p) noexcept;

/// Return a rvalue reference providing modifiable access to the element of
/// the specified `p`, having the (template parameter) `TYPE`.  This
/// function will not compile unless the types `T1` and `T2` are different
/// and the `TYPE` is the same as either `T1` or `T2`.
template<class TYPE, class T1, class T2>
TYPE&& get(bsl::pair<T1, T2>&& p) noexcept;

/// Return a rvalue reference providing non-modifiable access to the element
/// of the specified `p`, having the (template parameter) `TYPE`.  This
/// function will not compile unless the types `T1` and `T2` are different
/// and the `TYPE` is the same as either `T1` or `T2`.
template<class TYPE, class T1, class T2>
const TYPE&& get(const bsl::pair<T1, T2>&& p) noexcept;

}  // close bsl namespace

#endif // BSLS_COMPILERFEATURES_FULL_CPP11


// ============================================================================
//                      INLINE FUNCTION DEFINITIONS
// ============================================================================
// See IMPLEMENTATION NOTES in the .cpp before modifying anything below.

namespace bsl {
                        // -------------------
                        // struct Pair_ImpUtil
                        // -------------------
#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class ... ARGS>
inline
std::tuple<ARGS...>
Pair_ImpUtil::concatAllocator(
          std::tuple<ARGS...>&&          tpl,
          BloombergLP::bslma::Allocator *,
          bsl::Pair_BslmaIdiomNone)
{
    return std::move(tpl);
}

template <class ... ARGS>
inline
std::tuple<ARGS..., BloombergLP::bslma::Allocator *>
Pair_ImpUtil::concatAllocator(
                 std::tuple<ARGS...>&&          tpl,
                 BloombergLP::bslma::Allocator *alloc,
                 bsl::Pair_BslmaIdiomAtEnd)
{
    return std::tuple_cat(std::move(tpl), std::tie(alloc));
}

template <class ... ARGS>
inline
std::tuple<bsl::allocator_arg_t,
           BloombergLP::bslma::Allocator *,
           ARGS...>
Pair_ImpUtil::concatAllocator(
                 std::tuple<ARGS...>&&          tpl,
                 BloombergLP::bslma::Allocator *alloc,
                 bsl::Pair_BslmaIdiomAllocatorArgT)
{
    return std::tuple_cat(std::tie(bsl::allocator_arg, alloc), std::move(tpl));
}
#endif

                             // -----------------
                             // struct Pair_First
                             // -----------------

// CREATORS
#if !defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
inline
Pair_First<TYPE>::Pair_First()
: first()
{
}
#endif

template <class TYPE>
inline
Pair_First<TYPE>::Pair_First(BloombergLP::bslma::Allocator *,
                             Pair_BslmaIdiomNone)
: first()
{
}

template <class TYPE>
inline
Pair_First<TYPE>::Pair_First(BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAtEnd)
: first(basicAllocator)
{
}

template <class TYPE>
inline
Pair_First<TYPE>::Pair_First(BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAllocatorArgT)
: first(bsl::allocator_arg, basicAllocator)
{
}

template <class TYPE>
inline
BSLS_KEYWORD_CONSTEXPR
Pair_First<TYPE>::Pair_First(
                    typename bsl::add_lvalue_reference<const TYPE>::type value)
: first(value)
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_First<TYPE>::Pair_First(PARAM&& value)
: first(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(const PARAM& value)
: first(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM& value)
: first(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&&                        value,
                             BloombergLP::bslma::Allocator *,
                             Pair_BslmaIdiomNone)
: first(std::forward<PARAM>(value))
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&&                        value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAtEnd)
: first(std::forward<PARAM>(value), basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&&                        value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAllocatorArgT)
: first(bsl::allocator_arg, basicAllocator, std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(const PARAM&                   value,
                             BloombergLP::bslma::Allocator *,
                             Pair_BslmaIdiomNone)
: first(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&                         value,
                             BloombergLP::bslma::Allocator *,
                             Pair_BslmaIdiomNone)
: first(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(const PARAM&                   value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAtEnd)
: first(value, basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&                         value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAtEnd)
: first(value, basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(const PARAM&                   value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAllocatorArgT)
: first(bsl::allocator_arg, basicAllocator, value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE>::Pair_First(PARAM&                         value,
                             BloombergLP::bslma::Allocator *basicAllocator,
                             Pair_BslmaIdiomAllocatorArgT)
: first(bsl::allocator_arg, basicAllocator, value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ...ARGS, size_t ...I>
inline
Pair_First<TYPE>::Pair_First(std::tuple<ARGS...>&& argsPack,
                             BloombergLP::bslstl::Pair_IndexSequence<I...>)
: first(std::get<I>(std::move(argsPack))...)
{
}
#endif

                             // ------------------------
                             // struct Pair_First<TYPE&>
                             // ------------------------

// CREATORS

template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
inline
Pair_First<TYPE&>::Pair_First(TYPE& value)
: first(value)
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_First<TYPE&>::Pair_First(PARAM&& value)
: first(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE&>::Pair_First(const PARAM& value)
: first(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE&>::Pair_First(PARAM& value)
: first(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM>
inline constexpr
Pair_First<TYPE&>::Pair_First(PARAM&&                        value,
                              BloombergLP::bslma::Allocator *,
                              Pair_BslmaIdiomNone                 )
: first(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE&>::Pair_First(const PARAM&                   value,
                              BloombergLP::bslma::Allocator *,
                              Pair_BslmaIdiomNone                 )
: first(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_First<TYPE&>::Pair_First(PARAM&                         value,
                              BloombergLP::bslma::Allocator *,
                              Pair_BslmaIdiomNone                 )
: first(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ARG>
inline constexpr
Pair_First<TYPE&>::Pair_First(std::tuple<ARG>&& arg,
                              BloombergLP::bslstl::Pair_IndexSequence<0u>)
: first(std::get<0u>(arg))
{
}
#endif

// MANIPULATORS

template <class TYPE>
inline
Pair_First<TYPE&>& Pair_First<TYPE&>::operator=(const Pair_First& rhs)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    first = rhs.first;
    return *this;
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
                             // -------------------------
                             // struct Pair_First<TYPE&&>
                             // -------------------------

// CREATORS

template <class TYPE>
inline constexpr
Pair_First<TYPE&&>::Pair_First(TYPE&& value)
: first(std::move(value))
{
}

template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_First<TYPE&&>::Pair_First(PARAM&& value)
: first(std::forward<PARAM>(value))
{
}

template <class TYPE>
template <class PARAM>
inline constexpr
Pair_First<TYPE&&>::Pair_First(PARAM&&                        value,
                               BloombergLP::bslma::Allocator *,
                               Pair_BslmaIdiomNone                 )
: first(std::forward<PARAM>(value))
{
}

# if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ARG>
inline constexpr
Pair_First<TYPE&&>::Pair_First(std::tuple<ARG>&& arg,
                               BloombergLP::bslstl::Pair_IndexSequence<0u>)
: first(std::get<0u>(arg))
{
}
# endif

// MANIPULATORS
template <class TYPE>
inline
Pair_First<TYPE&&>& Pair_First<TYPE&&>::operator=(const Pair_First& rhs)
                                                                       noexcept
{
    first = rhs.first;
    return *this;
}
#endif

                             // ------------------
                             // struct Pair_Second
                             // ------------------

// CREATORS
#if !defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
inline
Pair_Second<TYPE>::Pair_Second()
: second()
{
}
#endif

template <class TYPE>
inline
Pair_Second<TYPE>::Pair_Second(BloombergLP::bslma::Allocator *,
                               Pair_BslmaIdiomNone)
: second()
{
}

template <class TYPE>
inline
Pair_Second<TYPE>::Pair_Second(BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAtEnd)
: second(basicAllocator)
{
}

template <class TYPE>
inline
Pair_Second<TYPE>::Pair_Second(BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAllocatorArgT)
: second(bsl::allocator_arg, basicAllocator)
{
}

template <class TYPE>
inline
BSLS_KEYWORD_CONSTEXPR
Pair_Second<TYPE>::Pair_Second(
                    typename bsl::add_lvalue_reference<const TYPE>::type value)
: second(value)
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_Second<TYPE>::Pair_Second(PARAM&& value)
: second(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(const PARAM& value)
: second(value)
{
}
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM& value)
: second(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&&                        value,
                               BloombergLP::bslma::Allocator *,
                               Pair_BslmaIdiomNone)
: second(std::forward<PARAM>(value))
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&&                        value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAtEnd)
: second(std::forward<PARAM>(value), basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&&                        value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAllocatorArgT)
: second(bsl::allocator_arg, basicAllocator, std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(const PARAM&                   value,
                               BloombergLP::bslma::Allocator *,
                               Pair_BslmaIdiomNone)
: second(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&                         value,
                               BloombergLP::bslma::Allocator *,
                               Pair_BslmaIdiomNone)
: second(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(const PARAM&                   value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAtEnd)
: second(value, basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&                         value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAtEnd)
: second(value, basicAllocator)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(const PARAM&                   value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAllocatorArgT)
: second(bsl::allocator_arg, basicAllocator, value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE>::Pair_Second(PARAM&                         value,
                               BloombergLP::bslma::Allocator *basicAllocator,
                               Pair_BslmaIdiomAllocatorArgT)
: second(bsl::allocator_arg, basicAllocator, value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ...ARGS, size_t ...I>
inline
Pair_Second<TYPE>::Pair_Second(std::tuple<ARGS...>&& argsPack,
                               BloombergLP::bslstl::Pair_IndexSequence<I...>)
: second(std::get<I>(std::move(argsPack))...)
{
}
#endif

                             // -------------------------
                             // struct Pair_Second<TYPE&>
                             // -------------------------

    // CREATORS
template <class TYPE>
BSLS_KEYWORD_CONSTEXPR
inline
Pair_Second<TYPE&>::Pair_Second(TYPE& value)
: second(value)
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_Second<TYPE&>::Pair_Second(PARAM&& value)
: second(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE&>::Pair_Second(const PARAM& value)
: second(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE&>::Pair_Second(PARAM& value)
: second(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class PARAM>
inline constexpr
Pair_Second<TYPE&>::Pair_Second(PARAM&&                        value,
                                BloombergLP::bslma::Allocator *,
                                Pair_BslmaIdiomNone                 )
: second(std::forward<PARAM>(value))
{
}
#else   // no C++11 support
template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE&>::Pair_Second(const PARAM&                   value,
                                BloombergLP::bslma::Allocator *,
                                Pair_BslmaIdiomNone                 )
: second(value)
{
}

template <class TYPE>
template <class PARAM>
inline
Pair_Second<TYPE&>::Pair_Second(PARAM&                         value,
                                BloombergLP::bslma::Allocator *,
                                Pair_BslmaIdiomNone                 )
: second(value)
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ARG>
inline constexpr
Pair_Second<TYPE&>::Pair_Second(std::tuple<ARG>&& arg,
                                BloombergLP::bslstl::Pair_IndexSequence<0u>)
: second(std::get<0u>(arg))
{
}
#endif

// MANIPULATORS
template <class TYPE>
inline
Pair_Second<TYPE&>& Pair_Second<TYPE&>::operator=(const Pair_Second& rhs)
                                                          BSLS_KEYWORD_NOEXCEPT
{
    second = rhs.second;
    return *this;
}


#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
                             // --------------------------
                             // struct Pair_Second<TYPE&&>
                             // --------------------------

// CREATORS
template <class TYPE>
inline constexpr
Pair_Second<TYPE&&>::Pair_Second(TYPE&& value)
: second(std::move(value))
{
}

template <class TYPE>
template <class PARAM, class>
inline constexpr
Pair_Second<TYPE&&>::Pair_Second(PARAM&& value)
: second(std::forward<PARAM>(value))
{
}

template <class TYPE>
template <class PARAM>
inline constexpr
Pair_Second<TYPE&&>::Pair_Second(PARAM&&                        value,
                                 BloombergLP::bslma::Allocator *,
                                 Pair_BslmaIdiomNone            )
: second(std::forward<PARAM>(value))
{
}

# if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class TYPE>
template <class ARG>
inline constexpr
Pair_Second<TYPE&&>::Pair_Second(std::tuple<ARG>&& arg,
                                 BloombergLP::bslstl::Pair_IndexSequence<0u>)
: second(std::get<0u>(arg))
{
}
# endif

// MANIPULATORS
template <class TYPE>
inline
Pair_Second<TYPE&&>& Pair_Second<TYPE&&>::operator=(const Pair_Second& rhs)
                                                                       noexcept
{
    second = rhs.second;
    return *this;
}
#endif
                                 // ----------
                                 // class pair
                                 // ----------

// CREATORS
#if !defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
inline
pair<T1, T2>::pair()
: FirstBase()
, SecondBase()
{
}
#endif

template <class T1, class T2>
inline
pair<T1, T2>::pair(BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(basicAllocator, FirstBslmaIdiom())
, SecondBase(basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
BSLS_KEYWORD_CONSTEXPR
inline
pair<T1, T2>::pair(typename bsl::add_lvalue_reference<const T1>::type a,
                   typename bsl::add_lvalue_reference<const T2>::type b)
: FirstBase(a)
, SecondBase(b)
{
}

template <class T1, class T2>
inline
pair<T1, T2>::pair(
            typename bsl::add_lvalue_reference<const T1>::type  a,
            typename bsl::add_lvalue_reference<const T2>::type  b,
            BloombergLP::bslma::Allocator                      *basicAllocator)
: FirstBase(a, basicAllocator, FirstBslmaIdiom())
, SecondBase(b, basicAllocator, SecondBslmaIdiom())
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
inline constexpr
pair<T1, T2>::pair(PARAM_1&& a, PARAM_2&& b)
: FirstBase(std::forward<PARAM_1>(a))
, SecondBase(std::forward<PARAM_2>(b))
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(PARAM_1&&                      a,
                   PARAM_2&&                      b,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(std::forward<PARAM_1>(a), basicAllocator, FirstBslmaIdiom())
, SecondBase(std::forward<PARAM_2>(b), basicAllocator, SecondBslmaIdiom())
{
}
#else   // no C++11 support
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const PARAM_1&                 a,
                   const PARAM_2&                 b,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(a, basicAllocator, FirstBslmaIdiom())
, SecondBase(b, basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(PARAM_1&                       a,
                   const PARAM_2&                 b,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(a, basicAllocator, FirstBslmaIdiom())
, SecondBase(b, basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const PARAM_1&                 a,
                   PARAM_2&                       b,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(a, basicAllocator, FirstBslmaIdiom())
, SecondBase(b, basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(PARAM_1&                       a,
                   PARAM_2&                       b,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(a, basicAllocator, FirstBslmaIdiom())
, SecondBase(b, basicAllocator, SecondBslmaIdiom())
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template<class ...ARGS_1, class ...ARGS_2>
inline
pair<T1, T2>::pair(std::piecewise_construct_t,
                   std::tuple<ARGS_1...>      first_args,
                   std::tuple<ARGS_2...>      second_args)
: FirstBase(std::move(first_args),
            BloombergLP::bslstl::Pair_MakeIndexSequence<
                   std::tuple_size<std::tuple<ARGS_1...> >::value
                                              >())
, SecondBase(std::move(second_args),
             BloombergLP::bslstl::Pair_MakeIndexSequence<
                   std::tuple_size<std::tuple<ARGS_2...> >::value
                                              >())
{
}

template <class T1, class T2>
template<class ...ARGS_1, class ...ARGS_2>
inline
pair<T1, T2>::pair(std::piecewise_construct_t,
                   std::tuple<ARGS_1...>          first_args,
                   std::tuple<ARGS_2...>          second_args,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(Pair_ImpUtil::concatAllocator(std::move(first_args),
                                          basicAllocator,
                                          FirstBslmaIdiom()),
            BloombergLP::bslstl::Pair_MakeIndexSequence<
                Pair_ConstructionParametersPackLength<T1, ARGS_1...>::value>())
, SecondBase(Pair_ImpUtil::concatAllocator(std::move(second_args),
                                           basicAllocator,
                                           SecondBslmaIdiom()),
            BloombergLP::bslstl::Pair_MakeIndexSequence<
                Pair_ConstructionParametersPackLength<T2, ARGS_2...>::value>())
{
}
#endif

template <class T1, class T2>
inline
pair<T1, T2>::pair(const pair&                    original,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(original.first, basicAllocator, FirstBslmaIdiom())
, SecondBase(original.second, basicAllocator, SecondBslmaIdiom())
{
}

#if defined (BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
inline
pair<T1, T2>::pair(pair&&                         original,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(std::forward<T1>(original.first),
            basicAllocator,
            FirstBslmaIdiom())
, SecondBase(std::forward<T2>(original.second),
             basicAllocator,
             SecondBslmaIdiom())
{
}
#else   // no C++11 support
template <class T1, class T2>
inline
pair<T1, T2>::pair(BloombergLP::bslmf::MovableRef<pair> original)
: FirstBase(MovUtil::move(MovUtil::access(original).first))
, SecondBase(MovUtil::move(MovUtil::access(original).second))
{
}

template <class T1, class T2>
inline
pair<T1, T2>::pair(BloombergLP::bslmf::MovableRef<pair>  original,
                   BloombergLP::bslma::Allocator        *basicAllocator)
: FirstBase(MovUtil::move(MovUtil::access(original).first),
            basicAllocator,
            FirstBslmaIdiom())
, SecondBase(MovUtil::move(MovUtil::access(original).second),
             basicAllocator,
             SecondBslmaIdiom())
{
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
inline constexpr
pair<T1, T2>::pair(const pair<PARAM_1, PARAM_2>& other)
: FirstBase(other.first)
, SecondBase(other.second)
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
inline constexpr
pair<T1, T2>::pair(const std::pair<PARAM_1, PARAM_2>& other)
: FirstBase(other.first)
, SecondBase(other.second)
{
}
#else   // no C++11 support
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const pair<PARAM_1, PARAM_2>& other)
: FirstBase(other.first)
, SecondBase(other.second)
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const std::pair<PARAM_1, PARAM_2>& other)
: FirstBase(other.first)
, SecondBase(other.second)
{
}
#endif

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const pair<PARAM_1, PARAM_2>&  other,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(other.first, basicAllocator, FirstBslmaIdiom())
, SecondBase(other.second, basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>::pair(const std::pair<PARAM_1, PARAM_2>&  other,
                   BloombergLP::bslma::Allocator      *basicAllocator)
: FirstBase(other.first, basicAllocator, FirstBslmaIdiom())
, SecondBase(other.second, basicAllocator, SecondBslmaIdiom())
{
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
constexpr pair<T1, T2>::pair(pair<PARAM_1, PARAM_2>&& other)
: FirstBase(std::move(other.first))
, SecondBase(std::move(other.second))
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
constexpr pair<T1, T2>::pair(std::pair<PARAM_1, PARAM_2>&& other)
: FirstBase(std::move(other.first))
, SecondBase(std::move(other.second))
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
pair<T1, T2>::pair(pair<PARAM_1, PARAM_2>&&       other,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(std::move(other.first), basicAllocator, FirstBslmaIdiom())
, SecondBase(std::move(other.second), basicAllocator, SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
pair<T1, T2>::pair(std::pair<PARAM_1, PARAM_2>&&  other,
                   BloombergLP::bslma::Allocator *basicAllocator)
: FirstBase(std::move(other.first), basicAllocator, FirstBslmaIdiom())
, SecondBase(std::move(other.second), basicAllocator, SecondBslmaIdiom())
{
}
#else   // no C++11 support
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
pair<T1, T2>::pair(
    BloombergLP::bslmf::MovableRef<pair<PARAM_1, PARAM_2> > other,
    BloombergLP::bslma::Allocator                          *basicAllocator)
: FirstBase(MovUtil::move(MovUtil::access(other).first),
            basicAllocator,
            FirstBslmaIdiom())
, SecondBase(MovUtil::move(MovUtil::access(other).second),
             basicAllocator,
             SecondBslmaIdiom())
{
}

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
pair<T1, T2>::pair(
   BloombergLP::bslmf::MovableRef<std::pair<PARAM_1, PARAM_2> > other,
   BloombergLP::bslma::Allocator                               *basicAllocator)
: FirstBase(MovUtil::move(MovUtil::access(other).first),
            basicAllocator,
            FirstBslmaIdiom())
, SecondBase(MovUtil::move(MovUtil::access(other).second),
             basicAllocator,
             SecondBslmaIdiom())
{
}
#endif

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
BSLS_KEYWORD_CONSTEXPR
pair<T1, T2>::pair(
         const BloombergLP::bslma::ManagedPtr_PairProxy<PARAM_1, PARAM_2>& rhs)
: FirstBase(rhs.first)
, SecondBase(rhs.second)
{
}
#endif // BDE_OMIT_INTERNAL_DEPRECATED

// MANIPULATORS
#if !defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
pair<T1, T2>& pair<T1, T2>::operator=(BloombergLP::bslmf::MovableRef<pair> rhs)
{
    pair& lvalue = rhs;
    first = MovUtil::move(lvalue.first);
    second = MovUtil::move(lvalue.second);
    return *this;
}
#endif



#if defined(BSLS_COMPILERFEATURES_FULL_CPP14)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
constexpr
pair<T1, T2>& pair<T1, T2>::operator=(const pair<PARAM_1, PARAM_2>& rhs)
{
    first = rhs.first;
    second = rhs.second;
    return *this;
}
#else   // no C++14 support
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline
pair<T1, T2>& pair<T1, T2>::operator=(const pair<PARAM_1, PARAM_2>& rhs)
{
    first = rhs.first;
    second = rhs.second;
    return *this;
}
#endif

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
BSLS_KEYWORD_CONSTEXPR_CPP14
pair<T1, T2>& pair<T1, T2>::operator=(pair<PARAM_1, PARAM_2>&& rhs)
{
    first = std::move(rhs.first);
    second = std::move(rhs.second);
    return *this;
}
#else   // no C++11 support
template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
pair<T1, T2>& pair<T1, T2>::operator=(
                   BloombergLP::bslmf::MovableRef<pair<PARAM_1, PARAM_2> > rhs)
{
    pair<PARAM_1, PARAM_2>& lvalue = rhs;
    first = MovUtil::move(lvalue.first);
    second = MovUtil::move(lvalue.second);
    return *this;
}
#endif

template <class T1, class T2>
template <class PARAM_1, class PARAM_2>
inline BSLS_KEYWORD_CONSTEXPR_CPP14
pair<T1, T2>&
pair<T1, T2>::operator=(const std::pair<PARAM_1, PARAM_2>& rhs)
{
    first = rhs.first;
    second = rhs.second;
    return *this;
}

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
template <class T1, class T2>
template <class PARAM_1, class PARAM_2, class>
inline
pair<T1, T2>::operator std::tuple<PARAM_1&, PARAM_2&>() noexcept
{
    return std::tuple<PARAM_1&, PARAM_2&>(first, second);
}

template <class T1, class T2>
template <class PARAM_1, class>
inline
pair<T1, T2>::operator std::tuple<PARAM_1&, decltype(std::ignore)&>() noexcept
{
    return std::tuple<PARAM_1&, decltype(std::ignore)&>(first, std::ignore);
}

template <class T1, class T2>
template <class PARAM_2, class>
inline
pair<T1, T2>::operator std::tuple<decltype(std::ignore)&, PARAM_2&>() noexcept
{
    return std::tuple<decltype(std::ignore)&, PARAM_2&>(std::ignore, second);
}
#endif


template <class T1, class T2>
inline
#if defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
void pair<T1, T2>::swap(pair& other)
    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(Pair_ImpUtil::hasNothrowSwap<T1, T2>())
#else   // has full C++11 support
void pair<T1, T2>::swap(pair& other)
                               noexcept(bsl::is_nothrow_swappable<T1>::value &&
                                        bsl::is_nothrow_swappable<T2>::value)
#endif
{
    // Find either 'std::swap' or a specialized 'swap' for 'T1' and 'T2' via
    // ADL.

    using std::swap;

    swap(first, other.first);
    swap(second, other.second);
}

// FREE OPERATORS
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator==(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return lhs.first == rhs.first && lhs.second == rhs.second;
}

#if !defined(BSLS_COMPILERFEATURES_SUPPORT_THREE_WAY_COMPARISON)
template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator!=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return ! (lhs == rhs);
}
#endif

#if defined(BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE)

template <class T1, class T2>
constexpr
std::common_comparison_category_t<
    BloombergLP::bslalg::SynthThreeWayUtil::Result<T1>,
    BloombergLP::bslalg::SynthThreeWayUtil::Result<T2>
> operator<=>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    using BloombergLP::bslalg::SynthThreeWayUtil;
    auto result = SynthThreeWayUtil::compare(lhs.first, rhs.first);
    return result == 0 ? SynthThreeWayUtil::compare(lhs.second, rhs.second)
                       : result;
}

#else

template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator<(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return (lhs.first < rhs.first ? true  :
            rhs.first < lhs.first ? false :
            lhs.second < rhs.second);
}

template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return rhs < lhs;
}

template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator<=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return ! (rhs < lhs);
}

template <class T1, class T2>
inline
BSLS_KEYWORD_CONSTEXPR
bool operator>=(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs)
{
    return ! (lhs < rhs);
}

#endif  // BSLALG_SYNTHTHREEWAYUTIL_AVAILABLE

// FREE FUNCTIONS
template <class T1, class T2>
inline
#if defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
void swap(pair<T1, T2>& a, pair<T1, T2>& b)
                       BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(noexcept(a.swap(b)))
{
    a.swap(b);
}
#else
bsl::enable_if_t<bsl::is_swappable<T1>::value && bsl::is_swappable<T2>::value>
swap(pair<T1, T2>& a, pair<T1, T2>& b) noexcept(noexcept(a.swap(b)))
{
    a.swap(b);
}
#endif

// HASH SPECIALIZATIONS
template <class HASHALG, class T1, class T2>
void hashAppend(HASHALG& hashAlg, const pair<T1, T2>&  input)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlg, input.first);
    hashAppend(hashAlg, input.second);
}

}  // close namespace bsl

#if defined(BSLS_COMPILERFEATURES_FULL_CPP11)
namespace BloombergLP {
namespace bslstl {

                         // ---------------------
                         // class Pair_GetImpUtil
                         // ---------------------

// CLASS METHODS
template <class T1, class T2>
inline
T1& Pair_GetImpUtil<0, T1, T2>::getPairElement(bsl::pair<T1, T2>& p) noexcept
{
    return p.first;
}

template <class T1, class T2>
inline
const T1&
Pair_GetImpUtil<0, T1, T2>::getPairElement(const bsl::pair<T1, T2>& p) noexcept
{
    return p.first;
}

template <class T1, class T2>
inline
T1&& Pair_GetImpUtil<0, T1, T2>::getPairElement(bsl::pair<T1, T2>&&  p)
                                                                       noexcept
{
    return std::move(p.first);
}

template <class T1, class T2>
inline
const T1&&
Pair_GetImpUtil<0, T1, T2>::getPairElement(const bsl::pair<T1, T2>&&  p)
                                                                       noexcept
{
    return std::move(p.first);
}

template <class T1, class T2>
inline
T2& Pair_GetImpUtil<1u, T1, T2>::getPairElement(bsl::pair<T1, T2>& p) noexcept
{
    return p.second;
}

template <class T1, class T2>
inline
const T2&
Pair_GetImpUtil<1u, T1, T2>::getPairElement(const bsl::pair<T1, T2>& p)
                                                                       noexcept
{
    return p.second;
}

template <class T1, class T2>
inline
T2&& Pair_GetImpUtil<1u, T1, T2>::getPairElement(bsl::pair<T1, T2>&&  p)
                                                                       noexcept
{
    return std::move(p.second);
}

template <class T1, class T2>
inline
const T2&&
Pair_GetImpUtil<1u, T1, T2>::getPairElement(const bsl::pair<T1, T2>&&  p)
                                                                       noexcept
{
    return std::move(p.second);
}

}  // close package namespace
}  // close enterprise namespace

// FREE FUNCTIONS
template<std::size_t INDEX, class T1, class T2>
inline
typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&
bsl::get(bsl::pair<T1, T2>& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<INDEX, T1, T2>::getPairElement(
                                                                            p);
}

template<std::size_t INDEX, class T1, class T2>
inline
const typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&
bsl::get(const bsl::pair<T1, T2>& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<INDEX, T1, T2>::getPairElement(
                                                                            p);
}

template<std::size_t INDEX, class T1, class T2>
inline
typename std::tuple_element<INDEX, bsl::pair<T1, T2> >::type&&
bsl::get(bsl::pair<T1, T2>&& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<INDEX, T1, T2>::getPairElement(
                                                                 std::move(p));
}

template<class TYPE, class T1, class T2>
inline
TYPE& bsl::get(bsl::pair<T1, T2>& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<
        BloombergLP::bslstl::Pair_IndexOfType<TYPE, T1, T2>::value, T1, T2>
                                                           ::getPairElement(p);
}

template<class TYPE, class T1, class T2>
inline
const TYPE& bsl::get(const bsl::pair<T1, T2>& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<
        BloombergLP::bslstl::Pair_IndexOfType<TYPE, T1, T2>::value, T1, T2>
                                                           ::getPairElement(p);
}

template<class TYPE, class T1, class T2>
inline
TYPE&& bsl::get(bsl::pair<T1, T2>&& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<
        BloombergLP::bslstl::Pair_IndexOfType<TYPE, T1, T2>::value, T1, T2>
            ::getPairElement(std::move(p));
}

template<class TYPE, class T1, class T2>
inline
const TYPE&& bsl::get(const bsl::pair<T1, T2>&& p) noexcept
{
    return BloombergLP::bslstl::Pair_GetImpUtil<
        BloombergLP::bslstl::Pair_IndexOfType<TYPE, T1, T2>::value, T1, T2>
            ::getPairElement(std::move(p));
}
#endif // BSLS_COMPILERFEATURES_FULL_CPP11

// ============================================================================
//                                TYPE TRAITS
// ============================================================================

namespace bsl {

template <class T1, class T2>
struct is_trivially_copyable<pair<T1, T2> >
    : bsl::integral_constant<bool, is_trivially_copyable<T1>::value
                                  && is_trivially_copyable<T2>::value>
{};

template <class T1, class T2>
struct is_trivially_default_constructible<bsl::pair<T1, T2> >
: bsl::integral_constant<bool, is_trivially_default_constructible<T1>::value
                            && is_trivially_default_constructible<T2>::value>
{};

}  // close namespace bsl

namespace BloombergLP {
namespace bslmf {

template <class T1, class T2>
struct IsBitwiseCopyable<bsl::pair<T1, T2> >
    : bsl::integral_constant<bool, IsBitwiseCopyable<T1>::value
                                && IsBitwiseCopyable<T2>::value>
{};

template <class T1, class T2>
struct IsPair<bsl::pair<T1, T2> > : bsl::true_type
{};

// Note that we must explicitly declare bitwise moveable sine 'T1' or 'T2' may
// be bitwise moveable and not bitwise copyable.

template <class T1, class T2>
struct IsBitwiseMoveable<bsl::pair<T1, T2> >
    : bsl::integral_constant<bool, bslmf::IsBitwiseMoveable<T1>::value
                                && bslmf::IsBitwiseMoveable<T2>::value>
{};

template <class T1, class T2>
struct IsBitwiseEqualityComparable<bsl::pair<T1, T2> >
: bsl::integral_constant<bool, bslmf::IsBitwiseEqualityComparable<T1>::value
                            && bslmf::IsBitwiseEqualityComparable<T2>::value
                            && sizeof(T1) + sizeof(T2) ==
                                           sizeof(bsl::pair<T1, T2>)>
{};

}  // close namespace bslmf

namespace bslma {

template <class T1, class T2>
struct UsesBslmaAllocator<bsl::pair<T1, T2> >
    : bsl::integral_constant<bool, UsesBslmaAllocator<T1>::value
                                || UsesBslmaAllocator<T2>::value>
{};

}  // close namespace bslma

}  // close enterprise namespace

#if defined(BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE)
# undef BSLSTL_PAIR_DO_NOT_SFINAE_TEST_IS_SWAPPABLE
#endif

#endif

// ----------------------------------------------------------------------------
// 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 ----------------------------------
