// bslstl_list.h                                                      -*-C++-*-
#ifndef INCLUDED_BSLSTL_LIST
#define INCLUDED_BSLSTL_LIST

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

//@PURPOSE: Provide an STL-compliant list class.
//
//@CLASSES:
//   bsl::list: STL-compatible list template
//
//@CANONICAL_HEADER: bsl_list.h
//
//@SEE_ALSO: bslstl_deque
//
//@DESCRIPTION: This component defines a single class template, 'bsl::list',
// implementing the standard container holding a sequence of elements (of a
// template parameter type, 'VALUE').  All list operations involving a single
// element are constant-time, including insertion and removal of an element
// anywhere in the list.  Operations that do not change the number of elements
// are performed without calling constructors, destructors, swap, or assignment
// on the individual elements.  (I.e., they are performed by
// pointer-manipulation alone.)  A 'list' does not provide random access to its
// elements; although access to the first and last elements of a 'list' is
// constant-time, other elements can be accessed only by traversing the list
// (forwards or backwards) from the beginning or end.
//
// An instantiation of 'list' is an allocator-aware, in-core value-semantic
// type whose salient attributes are its size (number of elements) and the
// sequence of its contained element values (in order).  If 'list' is
// instantiated with a type that is not itself value-semantic, then it will not
// retain all of its value-semantic qualities.  In particular, if a type cannot
// be tested for equality, then a 'list' containing that type cannot be tested
// for equality.  It is even possible to instantiate 'list' with a type that
// does not have a copy-constructor, in which case the 'list' will not be
// copyable.
//
// A 'list' meets the requirements of a sequence container with bidirectional
// iterators in the C++ standard [23.3].  The 'list' implemented here adheres
// to the C++11 standard when compiled with a C++11 compiler, and makes the
// best approximation when compiled with a C++03 compiler.  In particular, for
// C++03 we emulate move semantics, but limit forwarding (in 'emplace') to
// 'const' lvalues, and make no effort to emulate 'noexcept' or
// initializer-lists.
//
///Requirements on 'VALUE'
///-----------------------
// A 'list' is a fully Value-Semantic Type (see {'bsldoc_glossary'}) only if
// the supplied 'VALUE' template parameter is itself fully value-semantic.  It
// is possible to instantiate a 'list' with a 'VALUE' parameter argument that
// does not provide a full set of value-semantic operations, but then some
// methods of the container may not be instantiable.  The following
// terminology, adopted from the C++11 standard, is used in the function
// documentation of 'list' to describe a function's requirements for the
// 'VALUE' template parameter.  These terms are also defined in sections
// [utility.arg.requirements] and [container.requirements.general] of the C++11
// standard.
//
///Memory Allocation
///-----------------
// The type supplied as a list's 'ALLOCATOR' template parameter determines how
// that list will allocate memory.  The 'list' template supports allocators
// meeting the requirements of the C++11 standard [17.6.3.5]; in addition, it
// supports scoped-allocators derived from the 'bslma::Allocator' memory
// allocation protocol.  Clients intending to use 'bslma'-style allocators
// should use the template's default 'ALLOCATOR' type: The default type for the
// 'ALLOCATOR' template parameter, 'bsl::allocator', provides a C++11
// standard-compatible adapter for a 'bslma::Allocator' object.
//
///'bslma'-Style Allocators
/// - - - - - - - - - - - -
// If the (template parameter) type 'ALLOCATOR' of a 'list' instantiation is
// 'bsl::allocator', then objects of that list type will conform to the
// standard behavior of a 'bslma'-allocator-enabled type.  Such a list accepts
// an optional 'bslma::Allocator' argument at construction.  If the address of
// a 'bslma::Allocator' object is explicitly supplied at construction, the list
// uses it to supply memory for the list throughout its lifetime; otherwise,
// the list will use the default allocator installed at the time of the list's
// construction (see 'bslma_default').  In addition to directly allocating
// memory from the indicated 'bslma::Allocator', a list supplies that
// allocator's address to the constructors of contained objects of the
// (template parameter) 'VALUE' type if it defines the
// 'bslma::UsesBslmaAllocator' trait.
//
///Comparators and Strict Weak Ordering
/// - - - - - - - - - - - - - - - - - -
// A comparator function 'comp(a, b)' defines a *strict* *weak* *ordering* if
//: o 'comp(a, b)' && 'comp(b, c)' implies 'comp(a, c)'
//: o 'comp(a, b)' implies '!comp(b, a)'
//: o '!comp(a, b)' does not imply that 'comp(b, a)'
//
///Glossary
///--------
//..
//  Legend
//  ------
//  'X'    - denotes an allocator-aware container type (e.g., 'list')
//  'T'    - 'value_type' associated with 'X'
//  'A'    - type of the allocator used by 'X'
//  'm'    - lvalue of type 'A' (allocator)
//  'p',   - address ('T *') of uninitialized storage for a 'T' within an 'X'
//  'rv'   - rvalue of type (non-'const') 'T&&'
//  'v'    - rvalue or lvalue of type (possibly 'const') 'T'
//  'args' - 0 or more arguments
//..
// The following terms are used to more precisely specify the requirements on
// template parameter types in function-level documentation.
//
//: *default-insertable*: 'T' has a default constructor.  More precisely, 'T'
//:   is 'default-insertable' into 'X' means that the following expression is
//:   well-formed:
//:   'allocator_traits<A>::construct(m, p)'
//:
//: *move-insertable*: 'T' provides a constructor that takes an rvalue of type
//:   (non-'const') 'T'.  More precisely, 'T' is 'move-insertable' into 'X'
//:   means that the following expression is well-formed:
//:   'allocator_traits<A>::construct(m, p, rv)'
//:
//: *copy-insertable*: 'T' provides a constructor that takes an lvalue or
//:   rvalue of type (possibly 'const') 'T'.  More precisely, 'T' is
//:   'copy-insertable' into 'X' means that the following expression is
//:   well-formed:
//:   'allocator_traits<A>::construct(m, p, v)'
//:
//: *move-assignable*: 'T' provides an assignment operator that takes an rvalue
//:   of type (non-'const') 'T'.
//:
//: *copy-assignable*: 'T' provides an assignment operator that takes an lvalue
//:   or rvalue of type (possibly 'const') 'T'.
//:
//: *emplace-constructible*: 'T' is 'emplace-constructible' into 'X' from
//:   'args' means that the following expression is well-formed:
//:   'allocator_traits<A>::construct(m, p, args)'
//:
//: *erasable*: 'T' provides a destructor.  More precisely, 'T' is 'erasable'
//:   from 'X' means that the following expression is well-formed:
//:   'allocator_traits<A>::destroy(m, p)'
//:
//: *equality-comparable*: The type provides an equality-comparison operator
//:   that defines an equivalence relationship and is both reflexive and
//:   transitive.
//
///Operations
///----------
// This section describes the run-time complexity of operations on instances
// of 'list':
//..
//  Legend
//  ------
//  'V'             - template parameter 'VALUE' type of the list
//  'A'             - template parameter 'ALLOCATOR' type of the list
//  'a', 'b'        - distinct objects of type 'list<V>'
//  'k'             - unsigned integral constant
//  'ra','rb'       - distinct modifiable rvalue objects of type 'list<V>&&'
//  'n', 'm'        - number of elements in 'a' and 'b', respectively
//  'al'            - an STL-style memory allocator
//  'i1', 'i2'      - two iterators defining a sequence of 'V' objects
//  'v'             - an object of type 'V'
//  'rv'            - modifiable rvalue object of type 'V&&'
//  'p1', 'p2'      - two iterators belonging to 'a'
//  's1', 's2'      - two iterators belonging to 'b'
//  'pred'          - a unary predicate
//  'binary_pred'   - a binary predicate
//  'comp'          - a binary predicate implementing a strict-weak ordering
//  'args...'       - a variadic list of (up to 10) arguments
//  '{*}'           - C++11-style initializer list of length 'ni'
//  distance(i1,i2) - the number of elements in the range '[i1 .. i2)'
//
//  +----------------------------------------------------+--------------------+
//  | Operation                                          | Complexity         |
//  +====================================================+====================+
//  | list<V> a;    (default construction)               | O[1]               |
//  | list<V> a(al);                                     |                    |
//  +----------------------------------------------------+--------------------+
//  | list<V> a(b); (copy construction)                  | O[m]               |
//  | list<V> a(b, al);                                  |                    |
//  +----------------------------------------------------+--------------------+
//  | list<V> a(rb); (move construction)                 | O[1]               |
//  | list<V> a(rb, al);                                 | O[1] if 'a' and 'b'|
//  |                                                    | use the same       |
//  |                                                    | allocator,         |
//  |                                                    | O[m]     otherwise |
//  +----------------------------------------------------+--------------------+
//  | list<V> a(k);                                      | O[k]               |
//  | list<V> a(k, v);                                   |                    |
//  | list<V> a(k, v, al);                               |                    |
//  +----------------------------------------------------+--------------------+
//  | list<V> a(i1, i2);                                 | O[distance(i1, i2)]|
//  | list<V> a(i1, i2, al);                             |                    |
//  +----------------------------------------------------+--------------------+
//  | list<V> a({*}, al = A())                           | O[ni]              |
//  +----------------------------------------------------+--------------------+
//  | a.~list<V>(); (destruction)                        | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a = b;                (copy assignment)            | O[max(n, m)]       |
//  +----------------------------------------------------+--------------------+
//  | a = {*};              (copy assignment)            | O[max(n, ni)]      |
//  +----------------------------------------------------+--------------------+
//  | a = rb;               (move assignment)            | O[1] if 'a' and 'b'|
//  |                                                    | use the same       |
//  |                                                    | allocator,         |
//  |                                                    | O[max(n, m)]       |
//  |                                                    | otherwise          |
//  +----------------------------------------------------+--------------------+
//  | a.begin(), a.end(), a.cbegin(), a.cend(),          | O[1]               |
//  | a.rbegin(), a.rend(), a.crbegin(), a.crend()       |                    |
//  +----------------------------------------------------+--------------------+
//  | a == b, a != b                                     | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a < b, a <= b, a > b, a >= b                       | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.swap(b), swap(a, b)                              | O[1] if 'a' and 'b'|
//  |                                                    | use the same       |
//  |                                                    | allocator,         |
//  |                                                    | O[n + m] otherwise |
//  +----------------------------------------------------+--------------------+
//  | a.size()                                           | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.max_size()                                       | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.empty()                                          | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.get_allocator()                                  | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.emplace(p1, args...)                             | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.insert(p1, v)                                    | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.insert(p1, k, v)                                 | O[k]               |
//  +----------------------------------------------------+--------------------+
//  | a.insert(p1, i1, i2)                               | O[distance(i1, i2)]|
//  +----------------------------------------------------+--------------------+
//  | a.insert(p1, rv)                                   | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.insert(p1, {*})                                  | O[ni]              |
//  +----------------------------------------------------+--------------------|
//  | a.erase(p1)                                        | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.erase(p1, p2)                                    | O[distance(p1, p2)]|
//  +----------------------------------------------------+--------------------+
//  | a.clear()                                          | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.assign(i1,i2)                                    | O[distance(i1, i2)]|
//  +----------------------------------------------------+--------------------+
//  | a.assign(k, v)                                     | O[max(n, k)]       |
//  +----------------------------------------------------+--------------------+
//  | a.assign({*})                                      | O[max(n, ni)]      |
//  +----------------------------------------------------+--------------------+
//  | a.front(), a.back()                                | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.emplace_front(args...), a.emplace_back(args...)  | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.push_front(v),                                   |                    |
//  | a.push_back(v)                                     | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.push_front(rv),                                  |                    |
//  | a.push_back(rv)                                    | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.pop_front(), a.pop_back()                        | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.resize(k), a.resize(k, v)                        | O[k]               |
//  +----------------------------------------------------+--------------------+
//  | a.splice(p, b),  a.splice(p, b, s1)                | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.splice(p, rb), a.splice(p, rb, s1)               | O[1]               |
//  +----------------------------------------------------+--------------------+
//  | a.splice(p, b, s1, s2)                             | O[distance(s1, s2)]|
//  +----------------------------------------------------+--------------------+
//  | a.splice(p, rb, s1, s2)                            | O[distance(s1, s2)]|
//  +----------------------------------------------------+--------------------+
//  | a.remove(t), a.remove_if(pred)                     | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.unique(), a.unique(binary_pred)                  | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.merge(b),  a.merge(b, comp)                      | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.merge(rb), a.merge(rb, comp)                     | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | a.sort(), a.sort(comp)                             | O[n*log(n)]        |
//  +----------------------------------------------------+--------------------+
//  | a.reverse()                                        | O[n]               |
//  +----------------------------------------------------+--------------------+
//..
//
///Usage
///-----
// This section illustrates intended usage of this component.
//
///Example 1: Filter "Twinkle Star"
/// - - - - - - - - - - - - - - - -
// Suppose an observatory needs to analyze the results of a sky survey.  The
// raw data is a text file of star observations where each star is represented
// by a tuple of three numbers: (x, y, b), where x and y represent the angular
// coordinates of the star in the sky and b represents its brightness on a
// scale of 0 to 100.  A star having brightness 75 or higher is of particular
// interest, which is called "twinkle star".
//
// Our first example will read such a data file as described above, filter out
// the dim stars (brightness less than 75), and count the twinkle stars left
// in the list.  Our test data set has been selected such that there are 10
// stars in the set, of which 4 are sufficiently bright as to pass our filter.
//
// First, we define the class 'Star' that encapsulates a single tuple, and
// provides accessors functions 'x', 'y', and 'brightness', file I/O functions
// 'read' and 'write', and free operators '==', '!=', and '<':
//..
//  #include <cstdio>
//  using namespace std;
//
//  class Star
//      // This class represents a star as seen through a digital telescope.
//  {
//      // DATA
//      double d_x, d_y;     // coordinates
//
//      int    d_brightness; // brightness on a scale of 0 to 100
//
//  public:
//      // CREATORS
//      Star()
//          // Create a 'Star' object located at coordinates '(0, 0)' having
//          // '0' brightness.
//      : d_x(0), d_y(0), d_brightness(0)
//      {
//      }
//
//      Star(double x, double y, int b)
//          // Create a 'Star' object located at the specified coordinates
//          // '(x, y)' having the specified 'b' brightness.
//      : d_x(x), d_y(y), d_brightness(b)
//      {
//      }
//
//      // Compiler-generated copy construction, assignment, and destructor
//      // Star(const Star&) = default;
//      // Star& operator=(const Star&) = default;
//      // ~Star() = default;
//
//      // MANIPULATORS
//      bool read(FILE *input);
//          // Read x, y, and brightness from the specified 'input' file.
//          // Return 'true' if the read succeeded and 'false' otherwise.
//
//      void write(FILE *output) const;
//          // Write x, y, and brightness to the specified 'output' file
//          // followed by a newline.
//
//      // ACCESSORS
//      double x() const
//          // Return the x coordinate of this 'Star' object.
//      {
//          return d_x;
//      }
//
//      double y() const
//          // Return the y coordinate of this 'Star' object.
//      {
//          return d_y;
//      }
//
//      int brightness() const
//          // Return the brightness of this 'Star' object.
//      {
//          return d_brightness;
//      }
//  };
//
//  // FREE FUNCTIONS
//  bool operator==(const Star& lhs, const Star& rhs);
//  bool operator!=(const Star& lhs, const Star& rhs);
//  bool operator< (const Star& lhs, const Star& rhs);
//..
// Then, we define a 'readData' method that reads a file of data points and
// appends each onto a list.  The stars are stored in the data file in
// ascending sorted order by x and y coordinates.
//..
//  void readData(list<Star> *starList, FILE *input)
//  {
//      Star s;
//      while (s.read(input)) {
//          starList->push_back(s);
//      }
//  }
//..
// Now, we define the 'filter' method, which is responsible for removing stars
// with a brightness of less than 75 from the data set.  It does this by
// iterating over the list and erasing any element that does not pass the
// filter.  The list object features a fast 'erase' member function.  The
// return value of 'erase' is an iterator to the element immediately following
// the erased element:
//..
//  void filter(list<Star> *starList)
//  {
//      static const int threshold = 75;
//
//      list<Star>::iterator i = starList->begin();
//      while (i != starList->end()) {
//          if (i->brightness() < threshold) {
//              i = starList->erase(i);  // Erase and advance to next element.
//          }
//          else {
//              ++i;  // Advance to next element without erasing
//          }
//      }
//  }
//..
// Finally, we use the methods defined in above steps to put together our
// program to find twinkle stars:
//..
//  int usageExample1(int verbose)
//  {
//      FILE *input = fopen("star_data1.txt", "r");  // Open input file.
//      assert(input);
//
//      list<Star> starList;                         // Define a list of stars.
//      assert(starList.empty());                    // A list should be empty
//                                                   // after default
//                                                   // construction.
//
//      readData(&starList, input);                  // Read input to the list.
//      assert(10 == starList.size());               // Verify correct reading.
//      fclose(input);                               // Close input file.
//
//      filter(&starList);                           // Pick twinkle stars.
//      assert(4 == starList.size());                // Verify correct filter.
//
//      // Print out twinkle stars.
//      if (verbose) {
//          for (list<Star>::const_iterator i = starList.begin();
//               i != starList.end(); ++i) {
//              i->write(stdout);
//          }
//      }
//      return 0;
//  }
//..
//
///Example 2: Combine Two Star Surveys
///- - - - - - - - - - - - - - - - - -
// In the second example, we want to combine the results from two star surveys
// into a single list, using the same 'Star' class defined in the first usage
// example.
//
// First, we begin by reading both lists and filtering them.  (Our test data is
// selected so that the second data file contains 8 stars of which 3 are
// sufficiently bright as to pass our filter:
//..
//  int usageExample2(int verbose)
//  {
//      FILE *input = fopen("star_data1.txt", "r");  // Open first input file.
//      assert(input);
//
//      list<Star> starList1;                        // Define first star list.
//      assert(starList1.empty());
//
//      readData(&starList1, input);                 // Read input into list.
//      assert(10 == starList1.size());
//      fclose(input);                               // Close first input file.
//
//      input = fopen("star_data2.txt", "r");        // Open second input file.
//      assert(input);
//
//      list<Star> starList2;                        // Define second list.
//      assert(starList2.empty());
//
//      readData(&starList2, input);                 // Read input into list.
//      assert(8 == starList2.size());
//      fclose(input);                               // Close input file.
//
//      filter(&starList1);                          // Pick twinkle stars from
//                                                   // the first star list.
//      assert(4 == starList1.size());
//
//      filter(&starList2);                          // Pick twinkle stars from
//                                                   // the second star list.
//      assert(3 == starList2.size());
//..
// Then, we combine the two lists, 'starList1' and 'starList2'.  One way to do
// this is to simply insert the second list at the end of the first:
//..
//      list<Star> tmp1(starList1);  // Make a copy of the first list
//      list<Star> tmp2(starList2);  // Make a copy of the second list
//      tmp1.insert(tmp1.end(), tmp2.begin(), tmp2.end());
//      assert(7 == tmp1.size());    // Verify combined size.
//      assert(3 == tmp2.size());    // 'tmp2' should be unchanged.
//..
// Next, let's have a closer look of the above code and see if we can improve
// the combination performance.  The above 'insert' method appends a copy of
// each element in 'tmp2' onto the end of 'tmp1'.  This copy is unnecessary
// because we have no need for 'tmp2' after the lists have been combined.  A
// faster and less-memory-intensive technique is to use the 'splice' function,
// which *moves* rather than *copies* elements from one list to another:
//..
//      tmp1 = starList1;
//      tmp2 = starList2;
//      tmp1.splice(tmp1.begin(), tmp2);
//      assert(7 == tmp1.size());    // Verify combined size.
//      assert(0 == tmp2.size());    // 'tmp2' should be emptied by the splice.
//..
// Notice that, while the original lists were sorted in ascending order
// (because the data files were originally sorted), the combined list is no
// longer sorted.  To fix it, we sort 'tmp1' using the 'sort' member function:
//..
//      tmp1.sort();
//..
// Then, we suggest a third, and also the best approach to combine two lists,
// which is to take advantage of the fact that the lists were originally
// sorted, using the 'merge' function:
//..
//      starList1.merge(starList2);     // Merge 'starList2' into 'starList1'.
//      assert(7 == starList1.size());  // Verify combined size.
//      assert(0 == starList2.size());  // starList2 should be emptied by the
//                                      // merge.
//..
// Now, since the two star surveys may overlap, we want to eliminate
// duplicates.  We accomplish this by using the 'unique' member function:
//..
//      starList1.unique();             // Eliminate duplicates in 'starList1'.
//      assert(6 == starList1.size());  // Verify size after elimination.
//..
// Finally, we print the result:
//..
//      if (verbose) {
//          for (list<Star>::const_iterator i = starList1.begin();
//               i != starList1.end(); ++i) {
//              i->write(stdout);
//          }
//      }
//      return 0;
//  }
//..
// For completeness, the implementations of the 'read', 'write', and comparison
// functions for class 'Star' are shown below:
//..
//  bool Star::read(FILE *input)
//  {
//      int ret = fscanf(input, "%lf %lf %d", &d_x, &d_y, &d_brightness);
//      return 3 == ret;
//  }
//
//  void Star::write(FILE *output) const
//  {
//      fprintf(output, "%f %f %d\n", d_x, d_y, d_brightness);
//  }
//
//  bool operator==(const Star& lhs, const Star& rhs)
//  {
//      return lhs.x() == rhs.x()
//          && lhs.y() == rhs.y()
//          && lhs.brightness() == rhs.brightness();
//  }
//
//  bool operator!=(const Star& lhs, const Star& rhs)
//  {
//      return ! (lhs == rhs);
//  }
//
//  bool operator<(const Star& lhs, const Star& rhs)
//  {
//      if (lhs.x() < rhs.x())
//          return true;
//      else if (rhs.x() < lhs.x())
//          return false;
//      else if (lhs.y() < rhs.y())
//          return true;
//      else if (rhs.y() < lhs.y())
//          return true;
//      else
//          return lhs.brightness() < rhs.brightness();
//  }
//..

#include <bslscm_version.h>

#include <bslstl_algorithm.h>
#include <bslstl_iterator.h>
#include <bslstl_iteratorutil.h>

#include <bslalg_rangecompare.h>
#include <bslalg_typetraithasstliterators.h>

#include <bslma_allocator.h>
#include <bslma_allocatortraits.h>
#include <bslma_isstdallocator.h>
#include <bslma_stdallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_assert.h>
#include <bslmf_enableif.h>
#include <bslmf_isarithmetic.h>
#include <bslmf_isbitwisemoveable.h>
#include <bslmf_isconvertible.h>
#include <bslmf_isenum.h>
#include <bslmf_issame.h>
#include <bslmf_movableref.h>
#include <bslmf_nestedtraitdeclaration.h>
#include <bslmf_removecv.h>
#include <bslmf_typeidentity.h>
#include <bslmf_util.h>    // 'forward(V)'

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>
#include <bsls_performancehint.h>
#include <bsls_types.h>
#include <bsls_util.h>

#include <algorithm>   // for std::swap in C++03 or earlier

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
#include <initializer_list>
#endif

#include <utility>   // for std::swap in C++11 or later

#if BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
// Include version that can be compiled with C++03
// Generated on Thu Oct 21 10:11:37 2021
// Command line: sim_cpp11_features.pl bslstl_list.h
# define COMPILING_BSLSTL_LIST_H
# include <bslstl_list_cpp03.h>
# undef COMPILING_BSLSTL_LIST_H
#else

namespace bsl {

                        // =====================
                        // struct bsl::List_Node
                        // =====================

template <class VALUE>
class List_Node {
    // PRIVATE CLASS TEMPLATE.  For use only by 'bsl::list' implementation.
    // An instance of 'List_Node<T>' is a single node in a doubly-linked list
    // used to implement 'bsl::list<T,A>', for a given element type 'T' and
    // allocator type 'A'.  Note that an instantiation of this 'class' for a
    // given 'bsl::list' is independent of the allocator type.

    // DATA
    List_Node *d_prev_p;   // pointer to the previous node in the list
    List_Node *d_next_p;   // pointer to the next node in the list
    VALUE      d_value;    // list element

    // FRIENDS
    template <class LIST_VALUE, class LIST_ALLOCATOR>
    friend class list;

    template <class ITER_VALUE>
    friend class List_Iterator;

  private:
    // NOT IMPLEMENTED
    List_Node();                              // = delete;
    List_Node(const List_Node&);              // = delete;
    ~List_Node();                             // = delete;
    List_Node& operator=(const List_Node&);   // = delete;

    // A 'List_Node' is never constructed, copied, destroyed, or assigned to.
    // The 'd_value' field is constructed directly by 'list::emplace', and
    // destroyed directly by 'list::erase'.
};

                        // ========================
                        // class bsl::List_Iterator
                        // ========================

#if defined(BSLS_LIBRARYFEATURES_STDCPP_LIBCSTD)
// On Solaris studio12-v4, <algorithm> is compatible only with iterators
// inheriting from 'std::iterator'.

template <class VALUE>
class List_Iterator :
                 public std::iterator<std::bidirectional_iterator_tag, VALUE> {
#else
template <class VALUE>
class List_Iterator {
#endif
    // Implementation of 'bsl::list::iterator'.

    // PRIVATE TYPES
    typedef typename remove_cv<VALUE>::type  NcType;
    typedef List_Iterator<NcType>            NcIter;
    typedef List_Node<NcType>                Node;

    // DATA
    Node *d_node_p;      // pointer to list node

    // FRIENDS
    template <class LIST_VALUE, class LIST_ALLOCATOR>
    friend class list;

    template <class ITER_VALUE>    // This 'friend' statement is needed for the
    friend class List_Iterator;    // case where 'VALUE' != 'ITER_VALUE'.

    template <class T1, class T2>
    friend bool operator==(List_Iterator<T1>, List_Iterator<T2>);

  private:
    // PRIVATE ACCESSORS
    NcIter unconst() const;
        // Return an iterator providing modifiable access to the list element
        // that this list iterator refers to.

  public:
    // PUBLIC TYPES
    typedef std::bidirectional_iterator_tag   iterator_category;
    typedef NcType                            value_type;
    typedef BloombergLP::bsls::Types::IntPtr  difference_type;
    typedef VALUE                            *pointer;
    typedef VALUE&                            reference;

    // CREATORS
    List_Iterator();
        // Create a singular iterator (i.e., one that cannot be incremented,
        // decremented, or dereferenced until assigned a non-singular value).

    explicit List_Iterator(Node *nodePtr);
        // Create an iterator that references the value pointed to by the
        // specified 'nodePtr'.  If '0 == nodePtr' the iterator will be
        // singular.

    List_Iterator(const NcIter& other);                             // IMPLICIT
        // Create an iterator that has the same value as the specified 'other'
        // iterator.  If the (template parameter) type 'VALUE' is not
        // 'const'-qualified, then this constructor is the copy constructor;
        // otherwise, the copy constructor is implicitly generated.  Note that
        // this method is marked "IMPLICIT" in case it is not the copy
        // constructor.
        //
        // Note that this means that a 'List_Iterator<const VALUE>' can be copy
        // constructed or assigned to from a 'List_Iterator<VALUE>', but not
        // vice-versa.

    // Compiler-generated copy constructor, destructor, and copy-assignment
    // operator:
    //: o List_Iterator(const List_Iterator&); // Maybe defaulted (see above).
    //: o ~List_Iterator() = default;
    //: o List_Iterator& operator=(const List_Iterator&) = default;

#if defined(BSLS_COMPILERFEATURES_SUPPORT_DEFAULTED_FUNCTIONS)
    ~List_Iterator() = default;
        // Default compiler-generated destructor.

    List_Iterator& operator=(const List_Iterator&) = default;
        // Default compiler-generated copy-assignment operator.
#endif

    // MANIPULATORS
    List_Iterator& operator++();
        // Advance this iterator to the next element in the list and return its
        // new value.  The behavior is undefined unless this iterator is in the
        // range '[begin() .. end())' for some list (i.e., the iterator is not
        // singular, is not 'end()', and has not been invalidated).

    List_Iterator& operator--();
        // Regress this iterator to the previous element in the list and return
        // its new value.  The behavior is undefined unless this iterator is in
        // the range '(begin() .. end()]' for some list (i.e., the iterator is
        // not singular, is not 'begin()', and has not been invalidated).

    List_Iterator operator++(int);
        // Advance this iterator to the next element in the list and return its
        // previous value.  The behavior is undefined unless this iterator is
        // in the range '[begin() .. end())' for some list (i.e., the iterator
        // is not singular, is not 'end()', and has not been invalidated).

    List_Iterator operator--(int);
        // Regress this iterator to the previous element in the list and return
        // its previous value.  The behavior is undefined unless this iterator
        // is in the range '(begin() .. end()]' for some list (i.e., the
        // iterator is not singular, is not 'begin()', and has not been
        // invalidated).

    // ACCESSORS
    reference operator*() const;
        // Return a reference providing modifiable access to the element
        // referenced by this iterator.  The behavior is undefined unless this
        // iterator is in the range '[begin() .. end())' for some list (i.e.,
        // the iterator is not singular, is not 'end()', and has not been
        // invalidated).

    pointer operator->() const;
        // Return a pointer providing modifiable access to the element
        // referenced by this iterator.  The behavior is undefined unless this
        // iterator is in the range '[begin() .. end())' for some list (i.e.,
        // the iterator is not singular, is not 'end()', and has not been
        // invalidated).
};

// FREE OPERATORS
template <class T1, class T2>
bool operator==(List_Iterator<T1> lhs, List_Iterator<T2> rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators have the same
    // value, and 'false' otherwise.  Two iterators have the same value if both
    // refer to the same element of the same list or both are the 'end()'
    // iterator of the same list.  The behavior is undefined unless both 'lhs'
    // and 'rhs' refer to the same list.  Note that the different types 'T1'
    // and 'T2' are to facilitate comparisons between 'const' and non-'const'
    // iterators and there will be a compilation error if 'T1' and 'T2' differ
    // in any way other than 'const'-ness.

template <class T1, class T2>
bool operator!=(List_Iterator<T1> lhs, List_Iterator<T2> rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' iterators do not have the
    // same value, and 'false' otherwise.  Two iterators do not have the same
    // value unless both refer to the same element of the same list or unless
    // both are the 'end()' iterator of the same list.  The behavior is
    // undefined unless both 'lhs' and 'rhs' refer to the same list.  Note that
    // the different types 'T1' and 'T2' are to facilitate comparisons between
    // 'const' and non-'const' iterators and there will be a compilation error
    // if 'T1' and 'T2' differ in any way other than 'const'-ness.

                         // ===========================
                         // struct List_DefaultLessThan
                         // ===========================

template <class VALUE>
struct List_DefaultLessThan {
    // Binary predicate type for comparing two 'VALUE' objects using
    // 'operator<'.  This operation is usually, but not always, the same as
    // that provided by 'std::less<VALUE>'.  The standard requires that certain
    // functions use 'operator<', which means that divergent specializations of
    // 'std::less' are to be ignored.

    // ACCESSORS
    bool operator()(const VALUE& lhs, const VALUE& rhs) const;
        // Return 'true' if the value of the specified 'lhs' is less than that
        // of the specified 'rhs', and 'false' otherwise.
};

                        // ==============================
                        // class List_AllocAndSizeWrapper
                        // ==============================

template <class VALUE, class ALLOCATOR>
class List_AllocAndSizeWrapper : public allocator_traits<ALLOCATOR>::
                    template rebind_traits<List_Node<VALUE> >::allocator_type {
    // This struct is a wrapper around the allocator and size data members of a
    // 'list'.  It takes advantage of the empty-base optimization (EBO) so that
    // if the allocator is stateless, it takes up no space.
    //
    // TBD: This struct should eventually be replaced by the use of a general
    // EBO-enabled component that provides a 'pair'-like interface.  (A
    // properly-optimized 'tuple' would do the job.)

    // PRIVATE TYPES
    typedef List_Node<VALUE>                      Node;

    typedef typename allocator_traits<ALLOCATOR>::template rebind_traits<Node>
                                                  AllocTraits;
        // Alias for the allocator traits type associated with the 'bsl::list'
        // container.

    typedef typename AllocTraits::allocator_type  NodeAlloc;    // base class

    typedef typename AllocTraits::size_type       size_type;

    // DATA
    size_type d_size;      // Number of elements in the list (not including the
                           // sentinel).

  private:
    // NOT IMPLEMENTED
    List_AllocAndSizeWrapper(const List_AllocAndSizeWrapper&);
    List_AllocAndSizeWrapper& operator=(const List_AllocAndSizeWrapper&);

  public:
    // CREATORS
    List_AllocAndSizeWrapper(const NodeAlloc& basicAllocator,
                             size_type        size);
        // Create an allocator and size wrapper having the specified
        // 'basicAllocator' and (initial) 'size'.

    // ~List_AllocAndSizeWrapper() = default;

    // MANIPULATORS
    size_type& size();
        // Return a reference providing modifiable access to the 'size' field
        // of this object.

    // ACCESSORS
    const size_type& size() const;
        // Return a reference providing non-modifiable access to the 'size'
        // field of this object.
};

template <class VALUE, class ALLOCATOR>
class list;
    // Forward declaration required by 'List_NodeProctor'.

                            // ======================
                            // class List_NodeProctor
                            // ======================

template <class VALUE, class ALLOCATOR>
class List_NodeProctor {
    // This class provides a proctor to free a node containing an uninitialized
    // 'VALUE' object in the event that an exception is thrown.

    // PRIVATE TYPES
    typedef List_Node<VALUE>                      Node;

    typedef typename allocator_traits<ALLOCATOR>::template rebind_traits<Node>
                                                  AllocTraits;
        // Alias for the allocator traits type associated with the 'bsl::list'
        // container.

  public:
    // PUBLIC TYPES

    // In C++11 'NodePtr' would be generalized as follows:
    // 'typedef pointer_traits<VoidPtr>::template rebind<List_Node> NodePtr;'

    typedef typename AllocTraits::pointer         NodePtr;

  private:
    // DATA
    list<VALUE, ALLOCATOR>  *d_list_p;  // list to proctor
    NodePtr                  d_node_p;  // node to free upon destruction

  private:
    // NOT IMPLEMENTED
    List_NodeProctor(const List_NodeProctor&);
    List_NodeProctor &operator=(const List_NodeProctor&);

  public:
    // CREATORS
    List_NodeProctor(list<VALUE, ALLOCATOR> *listPtr, NodePtr nodePtr);
        // Create a node proctor object that will use the specified list
        // 'listPtr' to free the specified 'nodePtr'.  The behavior is
        // undefined unless 'nodePtr' was allocated by the allocator of
        // '*listPtr'.

    ~List_NodeProctor();
        // Destroy this node proctor, and free the node it contains unless the
        // 'release' method has been called before.  Note that the 'd_value'
        // field of the node is not destroyed.

    // MANIPULATORS
    void release();
        // Detach the node contained in this proctor from the proctor.  After
        // calling this 'release' method, the proctor no longer frees any node
        // upon its destruction.
};

                            // ===============
                            // class bsl::list
                            // ===============

template <class VALUE, class ALLOCATOR = bsl::allocator<VALUE> >
class list {
    // This class template implements a value-semantic container type holding a
    // sequence of elements of the (template parameter) 'VALUE' type.

    // PRIVATE TYPES
    typedef List_DefaultLessThan<VALUE>           DefaultLessThan;
        // Default comparator.

    typedef List_Node<VALUE>                      Node;
        // Alias for the node type in this list.

    typedef List_NodeProctor<VALUE, ALLOCATOR>    NodeProctor;
        // Proctor for guarding a newly allocated node.

    typedef typename allocator_traits<ALLOCATOR>::template rebind_traits<Node>
                                                  AllocTraits;
        // Alias for the allocator traits type associated with this container.

    typedef BloombergLP::bslmf::MovableRefUtil    MoveUtil;
        // Alias for the utility associated with movable references.

    typedef List_AllocAndSizeWrapper<VALUE, ALLOCATOR>
                                                  AllocAndSizeWrapper;
        // Alias for the wrapper containing the (usually stateless) allocator
        // and number of elements stored in this container.

    typedef typename AllocTraits::allocator_type  NodeAlloc;
        // Base class of 'List_AllocAndSizeWrapper' containing the allocator.

    // In C++11 'NodePtr' would be generalized as follows:
    // 'typedef pointer_traits<VoidPtr>::template rebind<List_Node> NodePtr;'

    typedef typename AllocTraits::pointer         NodePtr;

  public:
    // PUBLIC TYPES
    typedef VALUE&                                             reference;
    typedef const VALUE&                                       const_reference;
    typedef List_Iterator<VALUE>                               iterator;
    typedef List_Iterator<const VALUE>                         const_iterator;
    typedef typename allocator_traits<ALLOCATOR>::pointer      pointer;
    typedef typename allocator_traits<ALLOCATOR>::const_pointer
                                                               const_pointer;

    typedef typename allocator_traits<ALLOCATOR>::size_type    size_type;
    typedef typename allocator_traits<ALLOCATOR>::difference_type
                                                               difference_type;
    typedef VALUE                                 value_type;
    typedef ALLOCATOR                             allocator_type;
    typedef bsl::reverse_iterator<iterator>       reverse_iterator;
    typedef bsl::reverse_iterator<const_iterator> const_reverse_iterator;

  private:
    // DATA
    NodePtr             d_sentinel;        // node pointer of sentinel element
    AllocAndSizeWrapper d_alloc_and_size;  // node allocator

    // FRIENDS
    friend class List_NodeProctor<VALUE, ALLOCATOR>;

    // PRIVATE MANIPULATORS
    NodeAlloc& allocatorImp();
        // Return a reference providing modifiable access to the allocator used
        // to allocate nodes.

    NodePtr allocateNode();
        // Return a pointer to a node allocated from the container's allocator.
        // Before returning, the node's pointers are zeroed, but the
        // constructor of the 'value_type' is not called.

    void createSentinel();
        // Create the sentinel node of this list.  The sentinel node does not
        // hold a value.  When first created, its forward and backward pointers
        // point to itself, creating a circular linked list.  This function
        // also sets this list's size to 0.

    void deleteNode(NodePtr node);
        // Destroy the value part of the specified 'node' and free the node's
        // memory.  Do not do any pointer fix-up of the node or its neighbors,
        // and do not update 'sizeRef'.  The behavior is undefined unless
        // 'node' was allocated using the allocator of this list.

    void destroyAll();
        // Erase all elements and deallocate the sentinel node, leaving this
        // list in an invalid but destructible state (i.e., with 'size == -1').

    void freeNode(NodePtr node);
        // Zero out the pointers and deallocate the node pointed to by the
        // specified 'node'.  The behavior is undefined unless 'node' was
        // allocated using the allocator of this list.  Note that 'node's
        // destructor is not called, and, importantly, the value field of
        // 'node' is not destroyed.

    iterator insertNode(const_iterator position, NodePtr node);
        // Insert the specified 'node' prior to the specified 'position' in
        // this list.  The behavior is undefined unless 'node' was allocated
        // using the allocator of this list and 'position' is in the range
        // '[begin() .. end()]'.

    void linkNodes(NodePtr prev, NodePtr next);
        // Modify the forward pointer of the specified 'prev' to point to the
        // specified 'next' and the backward pointer of 'next' to point to
        // 'prev'.  The behavior is undefined unless 'prev' and 'next' point to
        // nodes created with 'allocateNode'.

    template <class COMPARE>
    NodePtr mergeImp(NodePtr node1,
                     NodePtr node2,
                     NodePtr finish,
                     COMPARE comparator);
        // Given a specified pair of nodes 'node1' and 'finish' specifying the
        // range '[node1 .. finish)', with the specified 'node2' pointing
        // somewhere in the middle of the sequence, merge sequence
        // '[node2 .. finish)' into '[node1 .. node2)', and return a pointer to
        // the beginning of the merged sequence, using the specified
        // 'comparator' to determine order.  If an exception is thrown, all
        // nodes remain in this list, but their order is unspecified.  If any
        // nodes in the range '[node1 .. node2)' compare equivalent to any
        // nodes in the range '[node2 .. finish)', the nodes from
        // '[node1 .. node2)' will be merged first.  The behavior is undefined
        // unless '[node1 .. node2)' and '[node2 .. finish)' each describe a
        // contiguous sequence of nodes.

    void quickSwap(list *other);
        // Efficiently exchange the value of this object with that of the
        // specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless this
        // object was created with the same allocator as 'other'.

    typename AllocTraits::size_type& sizeRef() BSLS_KEYWORD_NOEXCEPT;
        // Return a reference providing modifiable access to the data element
        // holding the size of this list.

    template <class COMPARE>
    NodePtr sortImp(NodePtr        *nodePtrPtr,
                    size_type       size,
                    const COMPARE&  comparator);
        // Sort the sequence of the specified 'size' nodes starting with the
        // specified '*nodePtrPtr', and modify '*nodePtrPtr' to refer to the
        // first node of the sorted sequence.  Return the pointer to the node
        // following the sequence of nodes to be sorted.  Use the specified
        // 'comparator' to compare 'VALUE' type objects.  If an exception is
        // thrown, all nodes remain properly linked, but their order is
        // unspecified.  The behavior is undefined unless '*nodePtrPtr' begins
        // a sequence of at least 'size' nodes, none of which is the sentinel
        // node, and '0 < size'.

    // PRIVATE ACCESSORS
    const NodeAlloc& allocatorImp() const;
        // Return a reference providing non-modifiable access to the allocator
        // used to allocate nodes.

    NodePtr headNode() const;
        // Return a pointer providing modifiable access to the first node in
        // this list or the sentinel node if this list is empty.

    const typename AllocTraits::size_type& sizeRef() const
                                                         BSLS_KEYWORD_NOEXCEPT;
        // Return a reference providing non-modifiable access to the data
        // element holding the size of this list.

  public:
    // CREATORS
    list();
        // Create an empty list.  A default-constructed object of the (template
        // parameter) type 'ALLOCATOR' is used.  If the type 'ALLOCATOR' is
        // 'bsl::allocator', the currently installed default allocator is used.

    explicit list(const ALLOCATOR& basicAllocator);
        // Create an empty list.  Use the specified 'basicAllocator' to supply
        // memory.  If the type 'ALLOCATOR' is 'bsl::allocator' (the default),
        // then 'basicAllocator' shall be convertible to 'bslma::Allocator *'.

    explicit list(size_type numElements);
        // Create a list of the specified 'numElements' size whose every
        // element is default-constructed.  A default-constructed object of the
        // (template parameter) type 'ALLOCATOR' is used.  If the type
        // 'ALLOCATOR' is 'bsl::allocator', the currently installed default
        // allocator is used.  Throw 'bsl::length_error' if
        // 'numElements > max_size()'.  This method requires that the (template
        // parameter) 'VALUE' be 'default-insertable' into this list (see
        // {Requirements on 'VALUE'}).

    list(size_type        numElements,
         const ALLOCATOR& basicAllocator);
        // Create a list of the specified 'numElements' size whose every
        // element is default-constructed.  Use the specified 'basicAllocator'
        // to supply memory.  If the type 'ALLOCATOR' is 'bsl::allocator' (the
        // default), then 'basicAllocator' shall be convertible to
        // 'bslma::Allocator *'.  Throw 'bsl::length_error' if
        // 'numElements > max_size()'.  This method requires that the (template
        // parameter) 'VALUE' be 'default-insertable' into this list (see
        // {Requirements on 'VALUE'}).

    list(size_type         numElements,
         const value_type& value,
         const ALLOCATOR&  basicAllocator = ALLOCATOR());
        // Create a list of the specified 'numElements' size whose every
        // element has the specified 'value'.  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is not
        // supplied, a default-constructed object of the (template parameter)
        // type 'ALLOCATOR' is used.  If the type 'ALLOCATOR' is
        // 'bsl::allocator' (the default), then 'basicAllocator', if supplied,
        // shall be convertible to 'bslma::Allocator *'.  If the type
        // 'ALLOCATOR' is 'bsl::allocator' and 'basicAllocator' is not
        // supplied, the currently installed default allocator is used.  Throw
        // 'bsl::length_error' if 'numElements > max_size()'.  This method
        // requires that the (template parameter) 'VALUE' be 'copy-insertable'
        // into this list (see {Requirements on 'VALUE'}).

    template <class INPUT_ITERATOR>
    list(INPUT_ITERATOR   first,
         INPUT_ITERATOR   last,
         const ALLOCATOR& basicAllocator = ALLOCATOR(),
         typename enable_if<
                 !is_arithmetic<INPUT_ITERATOR>::value &&
                 !is_enum<INPUT_ITERATOR>::value
         >::type * = 0)
        // Create a list initially containing copies of the values in the range
        // starting at the specified 'first' and ending immediately before the
        // specified 'last' iterators of the (template parameter) type
        // 'INPUT_ITERATOR'.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is not supplied, a
        // default-constructed object of the (template parameter) type
        // 'ALLOCATOR' is used.  If the type 'ALLOCATOR' is 'bsl::allocator'
        // (the default), then 'basicAllocator', if supplied, shall be
        // convertible to 'bslma::Allocator *'.  If the type 'ALLOCATOR' is
        // 'bsl::allocator' and 'basicAllocator' is not supplied, the currently
        // installed default allocator is used.  Throw 'bsl::length_error' if
        // the number of elements in '[first .. last)' exceeds the size
        // returned by 'max_size'.  The (template parameter) type
        // 'INPUT_ITERATOR' shall meet the requirements of an input iterator
        // defined in the C++11 standard [input.iterators] providing access to
        // values of a type convertible to 'value_type', and 'value_type' must
        // be 'emplace-constructible' from '*i' into this list, where 'i' is a
        // dereferenceable iterator in the range '[first .. last)' (see
        // {Requirements on 'VALUE'}).  The behavior is undefined unless
        // 'first' and 'last' refer to a sequence of valid values where 'first'
        // is at a position at or before 'last'.
    : d_alloc_and_size(basicAllocator, size_type(-1))
    {
        // MS Visual Studio 2008 compiler requires that a function using
        // enable_if be in-place inline.

        // '*this' is in an invalid but destructible state (size == -1).
        // Create a temporary list, 'tmp' with the specified data.  If an
        // exception is thrown, 'tmp's destructor will clean up.  Otherwise,
        // swap 'tmp' with '*this', leaving 'tmp' in an invalid but
        // destructible state and leaving '*this' fully constructed.

        list tmp(this->allocatorImp());
        tmp.insert(tmp.cbegin(), first, last);
        quickSwap(&tmp);
    }

    list(const list& original);
        // Create a list having the same value as the specified 'original'
        // object.  Use the allocator returned by
        // 'bsl::allocator_traits<ALLOCATOR>::
        // select_on_container_copy_construction(original.get_allocator())' to
        // allocate memory.  This method requires that the (template parameter)
        // type 'VALUE' be 'copy-insertable' into this list (see {Requirements
        // on 'VALUE'}).

    list(const list&                                    original,
         const typename type_identity<ALLOCATOR>::type& basicAllocator);
        // Create a list that has the same value as the specified 'original'
        // object.  Use the specified 'basicAllocator' to supply memory.  This
        // method requires that the (template parameter) 'VALUE' be
        // 'copy-insertable' into this list (see {Requirements on 'VALUE'}).
        // Note that a 'bslma::Allocator *' can be supplied for
        // 'basicAllocator' if the (template parameter) type 'ALLOCATOR' is
        // 'bsl::allocator' (the default).

    list(BloombergLP::bslmf::MovableRef<list> original);            // IMPLICIT
        // Create a list having the same value as the specified 'original'
        // object by moving (in constant time) the contents of 'original' to
        // the new list.  The allocator associated with 'original' is
        // propagated for use in the newly-created list.  'original' is left in
        // a valid but unspecified state.

    list(BloombergLP::bslmf::MovableRef<list>           original,
         const typename type_identity<ALLOCATOR>::type& basicAllocator);
        // Create a list having the same value as the specified 'original'
        // object that uses the specified 'basicAllocator' to supply memory.
        // The contents of 'original' are moved (in constant time) to the new
        // list if 'basicAllocator == original.get_allocator()', and are move-
        // inserted (in linear time) using 'basicAllocator' otherwise.
        // 'original' is left in a valid but unspecified state.  This method
        // requires that the (template parameter) 'VALUE' be 'move-insertable'
        // into this list (see {Requirements on 'VALUE'}).  Note that a
        // 'bslma::Allocator *' can be supplied for 'basicAllocator' if the
        // (template parameter) 'ALLOCATOR' is 'bsl::allocator' (the default).

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    list(std::initializer_list<value_type> values,
         const ALLOCATOR&                  basicAllocator = ALLOCATOR());
                                                                    // IMPLICIT
        // Create a list and append each 'value_type' object in the specified
        // 'values' initializer list.  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is not supplied, a
        // default-constructed object of the (template parameter) type
        // 'ALLOCATOR' is used.  If the type 'ALLOCATOR' is 'bsl::allocator'
        // (the default), then 'basicAllocator', if supplied, shall be
        // convertible to 'bslma::Allocator *'.  If the type 'ALLOCATOR' is
        // 'bsl::allocator' and 'basicAllocator' is not supplied, the currently
        // installed default allocator is used.  This method requires that the
        // (template parameter) 'VALUE' be 'copy-insertable' into this list
        // (see {Requirements on 'VALUE'}).
#endif

    ~list();
        // Destroy this list by calling the destructor for each element and
        // deallocating all allocated storage.

    // MANIPULATORS

                            // *** assignment ***

    list& operator=(const list& rhs);
        // Assign to this object the value of the specified 'rhs' object,
        // propagate to this object the allocator of 'rhs' if the 'ALLOCATOR'
        // type has trait 'propagate_on_container_copy_assignment', and return
        // a reference providing modifiable access to this object.  If an
        // exception is thrown, '*this' is left in a valid but unspecified
        // state.  This method requires that the (template parameter) type
        // 'VALUE' be 'copy-assignable' and 'copy-insertable' into this list.
        // Note that, to the extent possible, existing elements of this list
        // are copy-assigned to, to minimize the number of nodes that need to
        // be copy-inserted or erased.

    list& operator=(BloombergLP::bslmf::MovableRef<list> rhs)
        BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                          AllocTraits::is_always_equal::value);
        // Assign to this object the value of the specified 'rhs' object,
        // propagate to this object the allocator of 'rhs' if the 'ALLOCATOR'
        // type has trait 'propagate_on_container_move_assignment', and return
        // a reference providing modifiable access to this object.  The
        // contents of 'rhs' are moved (in constant time) to this list if
        // 'get_allocator() == rhs.get_allocator()' (after accounting for the
        // aforementioned trait); otherwise, all elements in this list are
        // either destroyed or move-assigned to and each additional element in
        // 'rhs' is move-inserted into this list.  'rhs' is left in a valid but
        // unspecified state, and if an exception is thrown, '*this' is left in
        // a valid but unspecified state.  This method requires that the
        // (template parameter) type 'VALUE' be 'move-assignable' and
        // 'move-insertable' into this list (see {Requirements on 'VALUE'}).

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    list& operator=(std::initializer_list<value_type> rhs);
        // Assign to this list, in order, the sequence of values in the
        // specified 'rhs' initializer list, and return a reference providing
        // modifiable access to this list.  This method requires that the
        // (template parameter) type 'VALUE' be 'copy-assignable' and
        // 'copy-insertable' into this list.  Note that, to the extent
        // possible, existing elements of this list are copy-assigned to, to
        // minimize the number of nodes that need to be copy-inserted or
        // erased.
#endif

    template <class INPUT_ITERATOR>
    void assign(INPUT_ITERATOR first,
                INPUT_ITERATOR last,
                typename enable_if<
                    !is_arithmetic<INPUT_ITERATOR>::value &&
                    !is_enum<INPUT_ITERATOR>::value
                >::type * = 0)
        // Assign to this list the sequence of values, in order, of the
        // elements of the specified range '[first .. last)'.  The (template
        // parameter) type 'INPUT_ITERATOR' shall meet the requirements of an
        // input iterator defined in the C++11 standard [24.2.3] providing
        // access to values of a type convertible to 'value_type', and
        // 'value_type' must be 'emplace-constructible' from '*i' into this
        // list, where 'i' is a dereferenceable iterator in the range
        // '[first .. last)'.  The behavior is undefined unless 'first' and
        // 'last' refer to a sequence of valid values where 'first' is at a
        // position at or before 'last'.  Note that, to the extent possible,
        // existing elements of this list are copy-assigned to, to minimize the
        // number of nodes that need to be copy-inserted or erased.  If an
        // exception is thrown, '*this' is left in a valid but unspecified
        // state.
    {
        // MS Visual Studio 2008 compiler requires that a function using
        // enable_if be in-place inline.

        iterator       dstIt  = this->begin();
        const iterator dstEnd = this->end();

        for (; first != last && dstEnd != dstIt; ++first, ++dstIt) {
            *dstIt = *first;
        }

        erase(dstIt, dstEnd);

        for (; first != last; ++first) {
            emplace(dstEnd, *first);
        }
    }

    void assign(size_type numElements, const value_type& value);
        // Replace the contents of this list with the specified 'numElements'
        // copies of the specified 'value'.  Note that, to the extent possible,
        // existing elements of this list are copy-assigned to, to minimize the
        // number of nodes that need to be copy-inserted or erased.

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    void assign(std::initializer_list<value_type> values);
        // Assign to this list, in order, the sequence of values in the
        // specified 'values' initializer list.  This method requires that the
        // (template parameter) type 'VALUE' be 'copy-assignable' and
        // 'copy-insertable' into this list.  Note that, to the extent
        // possible, existing elements of this list are copy-assigned to, to
        // minimize the number of nodes that need to be copy-inserted or
        // erased.
#endif

                              // *** iterators ***

    iterator begin() BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing modifiable access to the first element
        // in this list, and the past-the-end iterator if this list is empty.

    iterator end() BSLS_KEYWORD_NOEXCEPT;
        // Return the past-the-end (forward) iterator providing modifiable
        // access to this list.

    reverse_iterator rbegin() BSLS_KEYWORD_NOEXCEPT;
        // Return a reverse iterator providing modifiable access to the last
        // element in this list, and the past-the-end reverse iterator if this
        // list is empty.

    reverse_iterator rend() BSLS_KEYWORD_NOEXCEPT;
        // Return the past-the-end reverse iterator providing modifiable access
        // to this list.

                            // *** modify size ***

    void clear() BSLS_KEYWORD_NOEXCEPT;
        // Remove all the elements from this list.

    void resize(size_type newSize);
    void resize(size_type newSize, const value_type& value);
        // Change the size of this list to the specified 'newSize'.  Erase
        // 'size() - newSize' elements at the back if 'newSize < size()'.
        // Append 'newSize - size()' elements at the back having the optionally
        // specified 'value' if 'newSize > size()'; if 'value' is not
        // specified, default-constructed objects of the (template parameter)
        // 'VALUE' are emplaced.  This method has no effect if
        // 'newSize == size()'.  Throw 'bsl::length_error' if
        // 'newSize > max_size()'.

                           // *** element access ***

    reference back();
        // Return a reference providing modifiable access to the last element
        // of this list.  The behavior is undefined unless this list contains
        // at least one element.

    reference front();
        // Return a reference providing modifiable access to the first element
        // of this list.  The behavior is undefined unless this list contains
        // at least one element.

                                // *** end erase ***

    void pop_back();
        // Remove and destroy the last element of this list.  The behavior is
        // undefined unless this list contains at least one element.

    void pop_front();
        // Remove and destroy the first element of this list.  The behavior is
        // undefined unless this list contains at least one element.

                         // *** random access erase ***

    iterator erase(const_iterator position);
        // Remove from this list the element at the specified 'position', and
        // return an iterator providing modifiable access to the element
        // immediately following the removed element, or to the position
        // returned by the 'end' method if the removed element was the last in
        // the sequence.  The behavior is undefined unless 'position' refers to
        // an element in this list.

    iterator erase(const_iterator dstBegin, const_iterator dstEnd);
        // Remove from this list the elements starting at the specified
        // 'dstBegin' position up to, but not including, the specified 'dstEnd'
        // position, and return a non-'const' iterator equivalent to 'dstEnd'.
        // The behavior is undefined unless 'dstBegin' is an iterator in the
        // range '[begin() .. end()]' and 'dstEnd' is an iterator in the range
        // '[dstBegin .. end()]' (both endpoints included).  Note that
        // 'dstBegin' may be equal to 'dstEnd', in which case the list is not
        // modified.

                            // *** end inserts ***

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class... ARGS>
    reference emplace_back(ARGS&&... arguments);
        // Append to the back of this list a newly created 'value_type' object,
        // constructed by forwarding 'get_allocator()' (if required) and the
        // specified (variable number of) 'arguments' to the corresponding
        // constructor of 'value_type'.  Return a reference providing
        // modifiable access to the inserted element.  If an exception is
        // thrown (other than by the move constructor of a non-copy-insertable
        // 'value_type'), this method has no effect.  This method requires that
        // the (template parameter) 'VALUE' be 'move-insertable' into this list
        // and 'emplace-constructible' from 'arguments' (see {Requirements on
        // 'VALUE'}).
#endif

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class... ARGS>
    reference emplace_front(ARGS&&... arguments);
        // Prepend to the front of this list a newly created 'value_type'
        // object, constructed by forwarding 'get_allocator()' (if required)
        // and the specified (variable number of) 'arguments' to the
        // corresponding constructor of 'value_type'.  Return a reference
        // providing modifiable access to the inserted element.  If an
        // exception is thrown (other than by the move constructor of a
        // non-copy-insertable 'value_type'), this method has no effect.  This
        // method requires that the (template parameter) 'VALUE' be
        // 'move-insertable' into this list and 'emplace-constructible' from
        // 'arguments' (see {Requirements on 'VALUE'}).
#endif

    void push_back(const value_type& value);
        // Append to the back of this list a copy of the specified 'value'.
        // This method offers full guarantee of rollback in case an exception
        // is thrown.  This method requires that the (template parameter)
        // 'VALUE' be 'copy-constructible' (see {Requirements on 'VALUE'}).

    void push_back(BloombergLP::bslmf::MovableRef<value_type> value);
        // Append to the back of this list the specified move-insertable
        // 'value'.  'value' is left in a valid but unspecified state.  If an
        // exception is thrown (other than by the move constructor of a
        // non-copy-insertable 'value_type'), this method has no effect.  This
        // method requires that the (template parameter) 'VALUE' be
        // 'move-insertable' into this list (see {Requirements on 'VALUE'}).

    void push_front(const value_type& value);
        // Prepend to the front of this list a copy of the specified 'value'.
        // This method offers full guarantee of rollback in case an exception
        // is thrown.  This method requires that the (template parameter)
        // 'VALUE' be 'copy-constructible' (see {Requirements on 'VALUE'}).

    void push_front(BloombergLP::bslmf::MovableRef<value_type> value);
        // Prepend to the front of this list the specified move-insertable
        // 'value'.  'value' is left in a valid but unspecified state.  If an
        // exception is thrown (other than by the move constructor of a
        // non-copy-insertable 'value_type'), this method has no effect.  This
        // method requires that the (template parameter) 'VALUE' be
        // 'move-insertable' into this list (see {Requirements on 'VALUE'}).

                       // *** random access inserts ***

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
    template <class... ARGS>
    iterator emplace(const_iterator position, ARGS&&... arguments);
        // Insert at the specified 'position' in this list a newly created
        // 'value_type' object, constructed by forwarding 'get_allocator()' (if
        // required) and the specified (variable number of) 'arguments' to the
        // corresponding constructor of 'value_type', and return an iterator
        // providing modifiable access to the newly created and inserted
        // element.  If an exception is thrown (other than by the copy
        // constructor, move constructor, assignment operator, or move
        // assignment operator of 'value_type'), this method has no effect.
        // This method requires that the (template parameter) 'VALUE' be
        // 'move-insertable' into this list and 'emplace-constructible' from
        // 'arguments' (see {Requirements on 'VALUE'}).  The behavior is
        // undefined unless 'position' is an iterator in the range
        // '[cbegin() .. cend()]' (both endpoints included).
#endif

    iterator insert(const_iterator dstPosition, const value_type& value);
        // Insert at the specified 'dstPosition' in this list a copy of the
        // specified 'value', and return an iterator providing modifiable
        // access to the newly inserted element.  This method offers full
        // guarantee of rollback in case an exception is thrown other than by
        // the 'VALUE' copy constructor or assignment operator.  This method
        // requires that the (template parameter) 'VALUE' be 'copy-insertable'
        // into this list (see {Requirements on 'VALUE'}).  The behavior is
        // undefined unless 'dstPosition' is an iterator in the range
        // '[cbegin() .. cend()] (both endpoints included)'.

    iterator insert(const_iterator                             dstPosition,
                    BloombergLP::bslmf::MovableRef<value_type> value);
        // Insert at the specified 'dstPosition' in this list the specified
        // move-insertable 'value', and return an iterator providing modifiable
        // access to the newly inserted element.  'value' is left in a valid
        // but unspecified state.  If an exception is thrown (other than by the
        // copy constructor, move constructor, assignment operator, or move
        // assignment operator of 'value_type'), this method has no effect.
        // This method requires that the (template parameter) 'VALUE' be
        // 'move-insertable' into this list (see {Requirements on 'VALUE'}).
        // The behavior is undefined unless 'dstPosition' is an iterator in the
        // range '[cbegin() .. cend()]' (both endpoints included).

    iterator insert(const_iterator    dstPosition,
                    size_type         numElements,
                    const value_type& value);
        // Insert at the specified 'dstPosition' in this list the specified
        // 'numElements' copies of the specified 'value', and return an
        // iterator providing modifiable access to the first element in the
        // newly inserted sequence of elements.  This method requires that the
        // (template parameter) 'VALUE' be 'copy-insertable' into this list
        // (see {Requirements on 'VALUE'}).  The behavior is undefined unless
        // 'dstPosition' is an iterator in the range '[cbegin() .. cend()]'
        // (both endpoints included).

    template <class INPUT_ITERATOR>
    iterator insert(const_iterator dstPosition,
                    INPUT_ITERATOR first,
                    INPUT_ITERATOR last,
                    typename enable_if<
                        !is_arithmetic<INPUT_ITERATOR>::value &&
                        !is_enum<INPUT_ITERATOR>::value
                    >::type * = 0)
        // Insert at the specified 'dstPosition' in this list the values in the
        // range starting at the specified 'first' and ending immediately
        // before the specified 'last' iterators of the (template parameter)
        // type 'INPUT_ITERATOR', and return an iterator providing modifiable
        // access to the first element in the newly inserted sequence of
        // elements.  The (template parameter) type 'INPUT_ITERATOR' shall meet
        // the requirements of an input iterator defined in the C++11 standard
        // [input.iterators] providing access to values of a type convertible
        // to 'value_type', and 'value_type' must be 'emplace-constructible'
        // from '*i' into this list, where 'i' is a dereferenceable iterator in
        // the range '[first .. last)' (see {Requirements on 'VALUE'}).  The
        // behavior is undefined unless 'dstPosition' is an iterator in the
        // range '[cbegin() .. cend()]' (both endpoints included), and 'first'
        // and 'last' refer to a sequence of valid values where 'first' is at a
        // position at or before 'last'.
    {
        // MS Visual Studio 2008 compiler requires that a function using
        // enable_if be in place inline.

        if (first == last) {
            return dstPosition.unconst();                             // RETURN
        }

        // The return value should indicate the first node inserted.  We can't
        // assume 'INPUT_ITERATOR' has a post-increment available.

        iterator ret = insert(dstPosition, *first);
        for (++first; first != last; ++first) {
            insert(dstPosition, *first);
        }

        return ret;
    }

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
    iterator insert(const_iterator                    dstPosition,
                    std::initializer_list<value_type> values);
        // Insert at the specified 'dstPosition' in this list the value of each
        // 'value_type' object in the specified 'values' initializer list, and
        // return an iterator providing modifiable access to the first element
        // in the newly inserted sequence of elements.  This method requires
        // that the (template parameter) 'VALUE' be 'copy-insertable' into this
        // list (see {Requirements on 'VALUE'}).  The behavior is undefined
        // unless 'dstPosition' is an iterator in the range
        // '[cbegin() .. cend()]' (both endpoints included).
#endif

                          // *** list operations ***

    void merge(list&                                other);
    void merge(BloombergLP::bslmf::MovableRef<list> other);
        // Merge the specified sorted 'other' list into this sorted list.  This
        // method has no effect if 'other' is this list; otherwise, 'other' is
        // left empty.  The behavior is undefined unless both 'other' and this
        // list are sorted in non-decreasing order according to the ordering
        // provided by 'operator<', and unless both 'other' and this list use
        // the same allocator.  'operator<' must define a strict weak ordering
        // per 'value_type' (see {Comparators and Strict Weak Ordering}).

    template <class COMPARE>
    void merge(list& other, COMPARE comparator);
    template <class COMPARE>
    void merge(BloombergLP::bslmf::MovableRef<list> other, COMPARE comparator);
        // Merge the specified sorted 'other' list into this sorted list, using
        // the specified binary 'comparator' predicate to order elements.  This
        // method has no effect if 'other' is this list; otherwise, 'other' is
        // left empty.  The behavior is undefined unless both 'other' and this
        // list are sorted in non-decreasing order according to the ordering
        // provided by 'comparator', and unless both 'other' and this list use
        // the same allocator.

    void remove(const value_type& value);
        // Erase all the elements having the specified 'value' from this list.

    template <class PREDICATE>
    void remove_if(PREDICATE predicate);
        // Remove and destroy all elements in this list for which the specified
        // unary 'predicate' returns 'true'.

    void reverse() BSLS_KEYWORD_NOEXCEPT;
        // Reverse the order of the elements in this list.

    void sort();
        // Sort this list in non-decreasing order according to the order
        // provided by 'operator<'.  'operator<' must provide a strict weak
        // ordering over 'value_type' (see {Comparators and Strict Weak
        // Ordering}).  The sort is stable, meaning that if
        // '!(a < b) && !(b < a)', then the ordering of elements 'a' and 'b' in
        // the sequence is preserved.

    template <class COMPARE>
    void sort(COMPARE comparator);
        // Sort this list in non-decreasing order according to the order
        // provided by the specified 'comparator' predicate.  'comparator' must
        // define a strict weak ordering over 'value_type' (see {Comparators
        // and Strict Weak Ordering}).  The sort is stable, meaning that if
        // '!comparator(a, b) && !comparator(b, a)', then the ordering of
        // elements 'a' and 'b' in the sequence is preserved.

    void splice(const_iterator                       dstPosition,
                list&                                src);
    void splice(const_iterator                       dstPosition,
                BloombergLP::bslmf::MovableRef<list> src);
        // Remove all elements of the specified 'src' list and insert them, in
        // the same order, in this list at the specified 'dstPosition'.  The
        // behavior is undefined unless 'src' is not this list, this list and
        // 'src' use the same allocator, and 'dstPosition' is in the range
        // '[begin() .. end()]' (note both endpoints included).

    void splice(const_iterator                       dstPosition,
                list&                                src,
                const_iterator                       srcNode);
    void splice(const_iterator                       dstPosition,
                BloombergLP::bslmf::MovableRef<list> src,
                const_iterator                       srcNode);
        // Remove the single element at the specified 'srcNode' from the
        // specified 'src' list, and insert it at the specified 'dstPosition'
        // in this list.  The behavior is undefined unless 'srcNode' refers to
        // a valid element in 'src', this list and 'src' use the same
        // allocator, and 'dstPosition' is in the range '[begin() .. end()]'
        // (note both endpoints included).  Note that 'src' and '*this' may be
        // the same list, in which case the element is moved to a (possibly)
        // new position in the list.

    void splice(const_iterator                       dstPosition,
                list&                                src,
                const_iterator                       first,
                const_iterator                       last);
    void splice(const_iterator                       dstPosition,
                BloombergLP::bslmf::MovableRef<list> src,
                const_iterator                       first,
                const_iterator                       last);
        // Remove the elements in the specified range '[first .. last)' from
        // the specified 'src' list, and insert them, in the same order, at the
        // specified 'dstPosition' in this list.  The behavior is undefined
        // unless '[first .. last)' represents a range of valid elements in
        // 'src', 'dstPosition' is not in the range '[first .. last)', this
        // list and 'src' use the same allocator, and 'dstPosition' is in the
        // range '[begin() .. end()]' (note both endpoints included).  Note
        // that 'src' and '*this' may be the same list, in which case an entire
        // sequence of nodes is moved to a (possibly) new position in this
        // list.

    void unique();
        // Erase from this list all but the first element of every consecutive
        // group of elements that have the same value.

    template <class EQ_PREDICATE>
    void unique(EQ_PREDICATE binaryPredicate);
        // Erase from this list all but the first element of every consecutive
        // group of elements for which the specified 'binaryPredicate' returns
        // 'true' for any two consecutive elements in the group.

                              // *** misc ***

    void swap(list& other) BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(
                                          AllocTraits::is_always_equal::value);
        // Exchange the value of this object with that of the specified 'other'
        // object; also exchange the allocator of this object with that of
        // 'other' if the (template parameter) type 'ALLOCATOR' has the
        // 'propagate_on_container_swap' trait, and do not modify either
        // allocator otherwise.  This method provides the no-throw
        // exception-safety guarantee.  This operation has 'O[1]' complexity if
        // either this object was created with the same allocator as 'other' or
        // 'ALLOCATOR' has the 'propagate_on_container_swap' trait; otherwise,
        // it has 'O[n + m]' complexity, where 'n' and 'm' are the number of
        // elements in this object and 'other', respectively.  Note that this
        // method's support for swapping objects created with different
        // allocators when 'ALLOCATOR' does not have the
        // 'propagate_on_container_swap' trait is a departure from the
        // C++ Standard.

    // ACCESSORS

                               // *** iterators ***

    const_iterator begin() const BSLS_KEYWORD_NOEXCEPT;
    const_iterator cbegin() const BSLS_KEYWORD_NOEXCEPT;
        // Return an iterator providing non-modifiable access to the first
        // 'value_type' object in the ordered sequence of 'value_type' objects
        // maintained by this list, or the 'end' iterator if this list is
        // empty.

    const_iterator end() const BSLS_KEYWORD_NOEXCEPT;
    const_iterator cend() const BSLS_KEYWORD_NOEXCEPT;
        // Return the past-the-end (forward) iterator providing non-modifiable
        // access to this list.

    const_reverse_iterator rbegin() const BSLS_KEYWORD_NOEXCEPT;
    const_reverse_iterator crbegin() const BSLS_KEYWORD_NOEXCEPT;
        // Return a reverse iterator providing non-modifiable access to the
        // last element in this list, and the past-the-end reverse iterator if
        // this list is empty.

    const_reverse_iterator rend() const BSLS_KEYWORD_NOEXCEPT;
    const_reverse_iterator crend() const BSLS_KEYWORD_NOEXCEPT;
        // Return the past-the-end reverse iterator providing non-modifiable
        // access to this list.

                                  // *** size ***

    bool empty() const BSLS_KEYWORD_NOEXCEPT;
        // Return 'true' if this list has no elements, and 'false' otherwise.

    size_type max_size() const BSLS_KEYWORD_NOEXCEPT;
        // Return an upper bound on the largest number of elements that this
        // list could possibly hold.  Note that the return value of this
        // function does not guarantee that this list can successfully grow
        // that large, or even close to that large without running out of
        // resources.

    size_type size() const BSLS_KEYWORD_NOEXCEPT;
        // Return the number of elements in this list.

                           // *** element access ***

    const_reference back() const;
        // Return a reference providing non-modifiable access to the last
        // element of this list.  The behavior is undefined unless this list
        // contains at least one element.

    const_reference front() const;
        // Return a reference providing non-modifiable access to the first
        // element of this list.  The behavior is undefined unless this list
        // contains at least one element.

                                // *** misc ***

    allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT;
        // Return a copy of the allocator used for memory allocation by this
        // list.
};

#ifdef BSLS_COMPILERFEATURES_SUPPORT_CTAD
// CLASS TEMPLATE DEDUCTION GUIDES

template <
    class SIZE_TYPE,
    class VALUE,
    class ALLOC,
    class DEFAULT_ALLOCATOR = bsl::allocator<VALUE>,
    class = bsl::enable_if_t<
                            bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>,
    class = bsl::enable_if_t<
              bsl::is_convertible_v<
                 SIZE_TYPE,
                 typename bsl::allocator_traits<DEFAULT_ALLOCATOR>::size_type>>
    >
list(SIZE_TYPE, VALUE, ALLOC *) -> list<VALUE>;
    // Deduce the template parameter 'VALUE' from the corresponding parameter
    // supplied to the constructor of 'list'.  This deduction guide does not
    // participate unless the supplied allocator is convertible to
    // 'bsl::allocator<VALUE>'.

template <
    class INPUT_ITERATOR,
    class VALUE = typename
                   BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>
    >
list(INPUT_ITERATOR, INPUT_ITERATOR) -> list<VALUE>;
    // Deduce the template parameter 'VALUE' from the 'value_type' of the
    // iterators supplied to the constructor of 'list'.

template<
    class INPUT_ITERATOR,
    class ALLOCATOR,
    class VALUE = typename
                  BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class = bsl::enable_if_t<bsl::IsStdAllocator_v<ALLOCATOR>>>
list(INPUT_ITERATOR, INPUT_ITERATOR, ALLOCATOR) -> list<VALUE, ALLOCATOR>;
    // Deduce the template parameter 'VALUE' from the 'value_type' of the
    // iterators supplied to the constructor of 'list'.  Deduce the template
    // parameter 'ALLOCATOR' from the allocator supplied to the constructor of
    // 'list'.  This deduction guide does not participate unless the supplied
    // allocator meets the requirements of a standard allocator.

template<
    class INPUT_ITERATOR,
    class ALLOC,
    class VALUE = typename
                  BloombergLP::bslstl::IteratorUtil::IterVal_t<INPUT_ITERATOR>,
    class DEFAULT_ALLOCATOR = bsl::allocator<VALUE>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
list(INPUT_ITERATOR, INPUT_ITERATOR, ALLOC *)
-> list<VALUE>;
    // Deduce the template parameter 'VALUE' from the value_type of the
    // iterators supplied to the constructor of 'list'.  This deduction guide
    // does not participate unless the specified 'ALLOC' is convertible to
    // 'bsl::allocator<CHAR_TYPE>'.

template<
    class VALUE,
    class ALLOC,
    class DEFAULT_ALLOCATOR = bsl::allocator<VALUE>,
    class = bsl::enable_if_t<bsl::is_convertible_v<ALLOC *, DEFAULT_ALLOCATOR>>
    >
list(std::initializer_list<VALUE>, ALLOC *)
-> list<VALUE>;
    // Deduce the template parameter 'VALUE' from the value_type of the
    // intializer_list supplied to the constructor of 'list'.  This deduction
    // guide does not participate unless the specified 'ALLOC' is convertible
    // to 'bsl::allocator<CHAR_TYPE>'.
#endif

// FREE OPERATORS
template <class VALUE, class ALLOCATOR>
bool operator==(const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' objects have the same
    // value, and 'false' otherwise.  Two 'list' objects 'lhs' and 'rhs' have
    // the same value if they have the same number of elements, and each
    // element in the ordered sequence of elements of 'lhs' has the same value
    // as the corresponding element in the ordered sequence of elements of
    // 'rhs'.  This method requires that the (template parameter) type 'VALUE'
    // be 'equality-comparable' (see {Requirements on 'VALUE'}).

template <class VALUE, class ALLOCATOR>
bool operator!=(const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);


    // Return 'true' if the specified 'lhs' and 'rhs' objects do not have the
    // same value, and 'false' otherwise.  Two 'list' objects 'lhs' and 'rhs'
    // do not have the same value if they do not have the same number of
    // elements, or some element in the ordered sequence of elements of 'lhs'
    // does not have the same value as the corresponding element in the ordered
    // sequence of elements of 'rhs'.  This method requires that the
    // (template parameter) type 'VALUE' be 'equality-comparable' (see
    // {Requirements on 'VALUE'}).

template <class VALUE, class ALLOCATOR>
bool operator< (const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' list is
    // lexicographically less than that of the specified 'rhs' list, and
    // 'false' otherwise.  Given iterators 'i' and 'j' over the respective
    // sequences '[lhs.begin() .. lhs.end())' and '[rhs.begin() .. rhs.end())',
    // the value of list 'lhs' is lexicographically less than that of list
    // 'rhs' if 'true == *i < *j' for the first pair of corresponding iterator
    // positions where '*i < *j' and '*j < *i' are not both 'false'.  If no
    // such corresponding iterator position exists, the value of 'lhs' is
    // lexicographically less than that of 'rhs' if 'lhs.size() < rhs.size()'.
    // This method requires that 'operator<', inducing a total order, be
    // defined for 'value_type'.

template <class VALUE, class ALLOCATOR>
bool operator> (const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' list is
    // lexicographically greater than that of the specified 'rhs' list, and
    // 'false' otherwise.  The value of list 'lhs' is lexicographically greater
    // than that of list 'rhs' if 'rhs' is lexicographically less than 'lhs'
    // (see 'operator<').  This method requires that 'operator<', inducing a
    // total order, be defined for 'value_type'.  Note that this operator
    // returns 'rhs < lhs'.

template <class VALUE, class ALLOCATOR>
bool operator<=(const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' list is
    // lexicographically less than or equal to that of the specified 'rhs'
    // list, and 'false' otherwise.  The value of list 'lhs' is
    // lexicographically less than or equal to that of list 'rhs' if 'rhs' is
    // not lexicographically less than 'lhs' (see 'operator<').  This method
    // requires that 'operator<', inducing a total order, be defined for
    // 'value_type'.  Note that this operator returns '!(rhs < lhs)'.

template <class VALUE, class ALLOCATOR>
bool operator>=(const list<VALUE, ALLOCATOR>& lhs,
                const list<VALUE, ALLOCATOR>& rhs);
    // Return 'true' if the value of the specified 'lhs' list is
    // lexicographically greater than or equal to that of the specified 'rhs'
    // list, and 'false' otherwise.  The value of list 'lhs' is
    // lexicographically greater than or equal to that of list 'rhs' if 'lhs'
    // is not lexicographically less than 'rhs' (see 'operator<').  This method
    // requires that 'operator<', inducing a total order, be defined for
    // 'value_type'.  Note that this operator returns '!(lhs < rhs)'.

// FREE FUNCTIONS
template <class VALUE, class ALLOCATOR, class BDE_OTHER_TYPE>
typename list<VALUE, ALLOCATOR>::size_type
erase(list<VALUE, ALLOCATOR>& l, const BDE_OTHER_TYPE& value);
    // Erase all the elements in the specified list 'l' that compare equal to
    // the specified 'value'.  Return the number of elements erased.

template <class VALUE, class ALLOCATOR, class PREDICATE>
typename list<VALUE, ALLOCATOR>::size_type
erase_if(list<VALUE, ALLOCATOR>& l, PREDICATE predicate);
    // Erase all the elements in the specified list 'l' that satisfy the
    // specified predicate 'predicate'.  Return the number of elements erased.

template <class VALUE, class ALLOCATOR>
void swap(list<VALUE, ALLOCATOR>& a, list<VALUE, ALLOCATOR>& b)
    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(BSLS_KEYWORD_NOEXCEPT_OPERATOR(
                                                                   a.swap(b)));
    // Exchange the value of the specified 'a' object with that of the
    // specified 'b' object; also exchange the allocator of 'a' with that of
    // 'b' if the (template parameter) type 'ALLOCATOR' has the
    // 'propagate_on_container_swap' trait, and do not modify either allocator
    // otherwise.  This function provides the no-throw exception-safety
    // guarantee.  This operation has 'O[1]' complexity if either 'a' was
    // created with the same allocator as 'b' or 'ALLOCATOR' has the
    // 'propagate_on_container_swap' trait; otherwise, it has 'O[n + m]'
    // complexity, where 'n' and 'm' are the number of elements in 'a' and 'b',
    // respectively.  Note that this function's support for swapping objects
    // created with different allocators when 'ALLOCATOR' does not have the
    // 'propagate_on_container_swap' trait is a departure from the C++
    // Standard.

// ============================================================================
//                   INLINE AND TEMPLATE FUNCTION DEFINITIONS
// ============================================================================

                           // ------------------------
                           // class bsl::List_Iterator
                           // ------------------------

// PRIVATE ACCESSORS
template <class VALUE>
inline
typename List_Iterator<VALUE>::NcIter List_Iterator<VALUE>::unconst() const
{
    return NcIter(d_node_p);
}

// CREATORS
template <class VALUE>
inline
List_Iterator<VALUE>::List_Iterator()
: d_node_p()
{
}

template <class VALUE>
inline
List_Iterator<VALUE>::List_Iterator(Node *nodePtr)
: d_node_p(nodePtr)
{
}

template <class VALUE>
inline
List_Iterator<VALUE>::List_Iterator(const NcIter& other)
: d_node_p(other.d_node_p)
{
}

// MANIPULATORS
template <class VALUE>
inline
List_Iterator<VALUE>& List_Iterator<VALUE>::operator++()
{
    this->d_node_p = this->d_node_p->d_next_p;
    return *this;
}

template <class VALUE>
inline
List_Iterator<VALUE>& List_Iterator<VALUE>::operator--()
{
    this->d_node_p = this->d_node_p->d_prev_p;
    return *this;
}

template <class VALUE>
inline
List_Iterator<VALUE> List_Iterator<VALUE>::operator++(int)
{
    List_Iterator temp = *this;
    this->operator++();
    return temp;
}

template <class VALUE>
inline
List_Iterator<VALUE> List_Iterator<VALUE>::operator--(int)
{
    List_Iterator temp = *this;
    this->operator--();
    return temp;
}

// ACCESSORS
template <class VALUE>
inline
typename bsl::List_Iterator<VALUE>::reference
                                        List_Iterator<VALUE>::operator*() const
{
    return this->d_node_p->d_value;
}

template <class VALUE>
inline
typename bsl::List_Iterator<VALUE>::pointer
                                       List_Iterator<VALUE>::operator->() const
{
    return BloombergLP::bsls::Util::addressOf(this->d_node_p->d_value);
}

// FREE OPERATORS
template <class T1, class T2>
inline
bool operator==(bsl::List_Iterator<T1> lhs, bsl::List_Iterator<T2> rhs)
{
    // Make sure that this comparison will only compile if 'T1' and 'T2' match
    // except for a possible difference in 'const'-ness.

    BSLMF_ASSERT((bsl::is_same<typename bsl::remove_cv<T1>::type,
                               typename bsl::remove_cv<T2>::type>::value));

    return lhs.d_node_p == rhs.d_node_p;
}

template <class T1, class T2>
inline
bool operator!=(bsl::List_Iterator<T1> lhs, bsl::List_Iterator<T2> rhs)
{
    // Make sure that this comparison will only compile if 'T1' and 'T2' match
    // except for a possible difference in 'const'-ness.

    BSLMF_ASSERT((bsl::is_same<typename bsl::remove_cv<T1>::type,
                               typename bsl::remove_cv<T2>::type>::value));

    return ! (lhs == rhs);
}

                          // ------------------------------
                          // class List_AllocAndSizeWrapper
                          // ------------------------------

// CREATOR
template <class VALUE, class ALLOCATOR>
inline
List_AllocAndSizeWrapper<VALUE, ALLOCATOR>::List_AllocAndSizeWrapper(
                                               const NodeAlloc& basicAllocator,
                                               size_type        size)
: NodeAlloc(basicAllocator)
, d_size(size)
{
}

// MANIPULATORS
template <class VALUE, class ALLOCATOR>
inline
typename List_AllocAndSizeWrapper<VALUE, ALLOCATOR>::size_type&
List_AllocAndSizeWrapper<VALUE, ALLOCATOR>::size()
{
    return d_size;
}

// ACCESSORS
template <class VALUE, class ALLOCATOR>
inline
const typename List_AllocAndSizeWrapper<VALUE, ALLOCATOR>::size_type&
List_AllocAndSizeWrapper<VALUE, ALLOCATOR>::size() const
{
    return d_size;
}

                          // ----------------------
                          // class List_NodeProctor
                          // ----------------------

// CREATORS
template <class VALUE, class ALLOCATOR>
inline
List_NodeProctor<VALUE, ALLOCATOR>::List_NodeProctor(
                                               list<VALUE, ALLOCATOR> *listPtr,
                                               NodePtr                 nodePtr)
: d_list_p(listPtr)
, d_node_p(nodePtr)
{
    BSLS_ASSERT_SAFE(listPtr);
    BSLS_ASSERT_SAFE(nodePtr);
}

template <class VALUE, class ALLOCATOR>
inline
List_NodeProctor<VALUE, ALLOCATOR>::~List_NodeProctor()
{
    if (d_node_p) {
        d_list_p->freeNode(d_node_p);
    }
}

// MANIPULATORS
template <class VALUE, class ALLOCATOR>
inline
void List_NodeProctor<VALUE, ALLOCATOR>::release()
{
    d_node_p = 0;
}

                        // --------------------------
                        // class List_DefaultLessThan
                        // --------------------------

// ACCESSORS
template <class VALUE>
inline
bool List_DefaultLessThan<VALUE>::operator()(
                                      const VALUE& lhs, const VALUE& rhs) const
{
    return lhs < rhs;
}

                                // ---------------
                                // class bsl::list
                                // ---------------

// PRIVATE MANIPULATORS
template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::NodeAlloc&
                                         list<VALUE, ALLOCATOR>::allocatorImp()
{
    return d_alloc_and_size;  // implicit cast to base class
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::NodePtr list<VALUE, ALLOCATOR>::allocateNode()
{
    NodePtr ret = AllocTraits::allocate(allocatorImp(), 1);
    ret->d_prev_p = 0;
    ret->d_next_p = 0;
    return ret;
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::createSentinel()
{
    BSLS_ASSERT_SAFE(size_type(-1) == sizeRef() || 0 == sizeRef());

    d_sentinel = allocateNode();
    linkNodes(d_sentinel, d_sentinel);  // circular
    sizeRef() = 0;
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::deleteNode(NodePtr node)
{
    BSLS_ASSERT_SAFE(node);

    AllocTraits::destroy(allocatorImp(),
                         BloombergLP::bsls::Util::addressOf(node->d_value));
    AllocTraits::deallocate(allocatorImp(), node, 1);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::destroyAll()
{
    clear();
    freeNode(d_sentinel);
    sizeRef() = size_type(-1);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::freeNode(NodePtr node)
{
    AllocTraits::deallocate(allocatorImp(), node, 1);
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::insertNode(const_iterator position, NodePtr node)
{
    NodePtr next = position.d_node_p;
    NodePtr prev = next->d_prev_p;
    linkNodes(prev, node);
    linkNodes(node, next);
    ++sizeRef();
    return iterator(node);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::linkNodes(NodePtr prev, NodePtr next)
{
    prev->d_next_p = next;
    next->d_prev_p = prev;
}

template <class VALUE, class ALLOCATOR>
template <class COMPARE>
typename list<VALUE, ALLOCATOR>::NodePtr
list<VALUE, ALLOCATOR>::mergeImp(NodePtr node1,
                                 NodePtr node2,
                                 NodePtr finish,
                                 COMPARE comparator)
{
    NodePtr pre = node1->d_prev_p;

    // The only possible throwing operation is the comparator.  Exception
    // neutrality is achieved by ensuring that this list is in a valid state,
    // with no disconnected nodes, before the comparator is called.

    // Having the two sublists be contiguous parts of the same list has the
    // following advantages:
    //: 1 When we reach the end of a sublist, there is no "finalization" step
    //:   where the end of the remaining sublist must be spliced onto the
    //:   merged list.
    //: 2 No cleanup needed if an exception is thrown; the size and validity of
    //:   the resulting list needs no adjustment.

    while (node1 != node2 && node2 != finish) {
        // Loop invariants:
        // - The open range (pre, node1) is the current merged result
        // - The half-open range [node1, node2) is the 1st unmerged sequence
        // - The half-open range [node2, finish) is the 2nd unmerged sequence

        if (comparator(node2->d_value, node1->d_value)) {
            // 'node2' should come before 'node1'.

            // Find the end of the sequence of elements that belong before
            // node1 so that we can splice them all at once.

            NodePtr lastMove = node2;
            NodePtr next2    = node2->d_next_p;
            while (next2 != finish && comparator(next2->d_value,
                                                 node1->d_value)) {
                lastMove = next2;
                next2 = lastMove->d_next_p;
            }

            linkNodes(node2->d_prev_p, next2);
            linkNodes(node1->d_prev_p, node2);
            linkNodes(lastMove, node1);

            // Advance to next node in the 2nd unmerged sequence.

            node2 = next2;
        }
        else {
            // Advance to next node in the 1st unmerged sequence.

            node1 = node1->d_next_p;
        }
    }

    return pre->d_next_p;
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::quickSwap(list *other)
{
    BSLS_ASSERT_SAFE(allocatorImp() == other->allocatorImp());

    using std::swap;

    swap(d_sentinel, other->d_sentinel);
    swap(sizeRef(),  other->sizeRef());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::AllocTraits::size_type&
list<VALUE, ALLOCATOR>::sizeRef() BSLS_KEYWORD_NOEXCEPT
{
    return d_alloc_and_size.size();
}

template <class VALUE, class ALLOCATOR>
template <class COMPARE>
typename list<VALUE, ALLOCATOR>::NodePtr
list<VALUE, ALLOCATOR>::sortImp(NodePtr        *nodePtrPtr,
                                size_type       size,
                                const COMPARE&  comparator)
{
    BSLS_ASSERT(size > 0);

    NodePtr node1 = *nodePtrPtr;
    if (size < 2) {
        return node1->d_next_p;                                       // RETURN
    }

    size_type half = size / 2;

    NodePtr node2 = sortImp(&node1, half,        comparator);
    NodePtr next  = sortImp(&node2, size - half, comparator);

    *nodePtrPtr = mergeImp(node1, node2, next, comparator);
    return next;
}

// PRIVATE ACCESSORS
template <class VALUE, class ALLOCATOR>
inline
const typename list<VALUE, ALLOCATOR>::NodeAlloc&
                                   list<VALUE, ALLOCATOR>::allocatorImp() const
{
    return d_alloc_and_size;  // implicit cast to base class
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::NodePtr list<VALUE, ALLOCATOR>::headNode()
                                                                          const
{
    return d_sentinel->d_next_p;
}

template <class VALUE, class ALLOCATOR>
inline
const typename list<VALUE, ALLOCATOR>::AllocTraits::size_type&
list<VALUE, ALLOCATOR>::sizeRef() const BSLS_KEYWORD_NOEXCEPT
{
    return d_alloc_and_size.size();
}

// CREATORS
template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list()
: d_sentinel()
, d_alloc_and_size(ALLOCATOR(), 0)
{
    BSLMF_ASSERT((bsl::is_same<size_type,
                                     typename AllocTraits::size_type>::value));
    BSLMF_ASSERT((bsl::is_same<difference_type,
                               typename AllocTraits::difference_type>::value));
    createSentinel();
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(const ALLOCATOR& basicAllocator)
: d_sentinel()
, d_alloc_and_size(basicAllocator, 0)
{
    createSentinel();
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(size_type numElements)
: d_sentinel()
, d_alloc_and_size(ALLOCATOR(), size_type(-1))
{
    // '*this' is in an invalid but destructible state (size == -1).

    list tmp(this->allocatorImp());

    // Default-construct (value-initialize) 'n' elements into 'tmp'.  'tmp's
    // destructor will clean up if an exception is thrown.

    iterator pos = tmp.end();
    for (size_type i = 0; i < numElements; ++i) {
        tmp.emplace(pos);
    }

    quickSwap(&tmp);  // Leave 'tmp' in an invalid but destructible state.
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(size_type        numElements,
                             const ALLOCATOR& basicAllocator)
: d_sentinel()
, d_alloc_and_size(basicAllocator, size_type(-1))
{
    // '*this' is in an invalid but destructible state (size == -1).

    list tmp(this->allocatorImp());

    // Default-construct (value-initialize) 'n' elements into 'tmp'.  'tmp's
    // destructor will clean up if an exception is thrown.

    const_iterator pos = tmp.cend();
    for (size_type i = 0; i < numElements; ++i) {
        tmp.emplace(pos);
    }

    quickSwap(&tmp);  // Leave 'tmp' in an invalid but destructible state.
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(size_type        numElements,
                             const VALUE&     value,
                             const ALLOCATOR& basicAllocator)
: d_sentinel()
, d_alloc_and_size(basicAllocator, size_type(-1))
{
    // '*this' is in an invalid but destructible state (size == -1).

    list tmp(this->allocatorImp());
    tmp.insert(tmp.cbegin(), numElements, value);    // 'tmp's destructor will
                                                     // clean up on throw.
    quickSwap(&tmp);      // Leave 'tmp' in an invalid but destructible state.
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(const list& original)
: d_sentinel()
, d_alloc_and_size(
   AllocTraits::select_on_container_copy_construction(original.allocatorImp()),
   size_type(-1))
{
    list tmp(this->allocatorImp());

    tmp.insert(tmp.cbegin(), original.begin(), original.end());

    quickSwap(&tmp);  // Leave 'tmp' in an invalid but destructible state.
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(const list&                        original,
                 const typename type_identity<ALLOCATOR>::type& basicAllocator)
: d_sentinel()
, d_alloc_and_size(basicAllocator, size_type(-1))
{
    list tmp(this->allocatorImp());

    tmp.insert(tmp.cbegin(), original.begin(), original.end());

    quickSwap(&tmp);  // Leave 'tmp' in an invalid but destructible state.
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(BloombergLP::bslmf::MovableRef<list> original)
: d_sentinel()
, d_alloc_and_size(MoveUtil::access(original).allocatorImp(), 0)
{
    // Allocator should be copied, not moved, to ensure identical allocators
    // between this and 'original', otherwise 'swap' is undefined.

    // An rvalue must be left in a valid state after a move.

    createSentinel();

    // '*this' is now in a valid state.

    quickSwap(&MoveUtil::access(original));
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::list(
                 BloombergLP::bslmf::MovableRef<list>           original,
                 const typename type_identity<ALLOCATOR>::type& basicAllocator)
: d_sentinel()
, d_alloc_and_size(basicAllocator, size_type(-1))
{
    // '*this' is in an invalid but destructible state (size == -1).

    list& lvalue = original;
    if (this->allocatorImp() == lvalue.allocatorImp()) {
        // An rvalue must be left in a valid state after a move.

        createSentinel();      // '*this' is now in a valid state.
        quickSwap(&lvalue);
    }
    else {
        // different allocators, must copy

        list tmp(this->allocatorImp());

        // Avoid relying on VALUE's copy c'tor unless no move c'tor is
        // available.

        NodePtr endPtr = lvalue.d_sentinel;
        for (NodePtr p = lvalue.headNode(); endPtr != p;  p = p->d_next_p) {
            tmp.emplace_back(MoveUtil::move(p->d_value));
        }

        quickSwap(&tmp);  // Leave 'tmp' in an invalid but destructible state.
    }
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class VALUE, class ALLOCATOR>
inline
list<VALUE, ALLOCATOR>::list(std::initializer_list<VALUE> values,
                             const ALLOCATOR&             basicAllocator)
: d_alloc_and_size(basicAllocator, size_type(-1))
{
    // '*this' is in an invalid but destructible state (size == -1).  Create a
    // temporary list, 'tmp', with the specified data.  If an exception is
    // thrown, 'tmp's destructor will clean up.  Otherwise, swap 'tmp' with
    // '*this', leaving 'tmp' in an invalid but destructible state and leaving
    // '*this' fully constructed.

    list tmp(this->allocatorImp());
    tmp.insert(tmp.cbegin(), values.begin(), values.end());

    quickSwap(&tmp);
}
#endif

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>::~list()
{
    // A size of -1 means a special incompletely-initialized state with no
    // sentinel, which requires no destruction.

    if (sizeRef() != size_type(-1)) {
        destroyAll();
    }
}

// MANIPULATORS

                            // *** assignment ***

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>& list<VALUE, ALLOCATOR>::operator=(const list& rhs)
{
    if (this == &rhs) {
        return *this;                                                 // RETURN
    }

    if (AllocTraits::propagate_on_container_copy_assignment::value
       && allocatorImp() != rhs.allocatorImp()) {
        // We can't simply swap containers, as we aren't allowed to modify
        // 'rhs'.

        // Completely destroy and rebuild list using new allocator.

        // Create a new list with the new allocator.  This operation might
        // throw, so we do it before destroying the old list.

        list tmp(rhs, rhs.allocatorImp());

        // Clear existing list and leave in an invalid but destructible state.

        destroyAll();

        // Assign allocator (here we are relying on the C++11 standard, which
        // requires that the allocator type not throw on copy or assign) as
        // 'quickSwap' requires the entities to have the same allocator.

        allocatorImp() = rhs.allocatorImp();

        // Now swap lists, leaving 'tmp' in an invalid but destructible state.

        quickSwap(&tmp);
    }
    else {
        assign(rhs.begin(), rhs.end());
    }

    return *this;
}

template <class VALUE, class ALLOCATOR>
list<VALUE, ALLOCATOR>& list<VALUE, ALLOCATOR>::operator=(
                                      BloombergLP::bslmf::MovableRef<list> rhs)
    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(AllocTraits::is_always_equal::value)
{
    list& lvalue = rhs;

    if (this == &lvalue) {
        return *this;                                                 // RETURN
    }

    if (this->allocatorImp() == lvalue.allocatorImp()) {
        // Equal allocators, just swap contents, will never throw.

        quickSwap(&lvalue);
    }
    else if (AllocTraits::propagate_on_container_move_assignment::value) {
        // An rvalue must be left in a valid state after a move.  Both '*this'
        // and 'rhs' must be left in valid states after a throw.

        // Note: tearing everything down, then changing the allocator, then
        // doing 'quickSwap(&lvalue)' has a problem in that it could leave
        // 'rhs' in an invalid state, since if 'this->createSentinel()' were
        // called after the tearing down to render '*this' to a valid value,
        // 'createSentinel' might throw, leaving '*this' in an invalid state.

        // Swap everything, including the allocator (here we are relying on the
        // C++11 standard, which requires that the allocator type not throw on
        // copy or assign).

        list other(MoveUtil::move(lvalue));

        using std::swap;

        swap(allocatorImp(), other.allocatorImp()); // won't throw
        swap(d_sentinel,     other.d_sentinel);     // swap of pointer type
        swap(sizeRef(),      other.sizeRef());      // swap of fundamental type
    }
    else {
        // Unequal allocators and the allocator of the destination is to remain
        // unchanged.  Copy using 'move', which will use copy functions where
        // 'value_type' doesn't support moving.  Note that if this throws part
        // way through, both '*this' and 'rhs' may be left changed.

        NodePtr              dstPtr    = this->headNode();
        const const_iterator dstEnd    = this->cend();
        const NodePtr        dstEndPtr = dstEnd.d_node_p;

        NodePtr              srcPtr    = lvalue.headNode();
        const NodePtr        srcEndPtr = lvalue.d_sentinel;

        for (; srcEndPtr != srcPtr && dstEndPtr != dstPtr;
                        srcPtr = srcPtr->d_next_p, dstPtr = dstPtr->d_next_p) {
            dstPtr->d_value = MoveUtil::move(srcPtr->d_value);
        }

        erase(const_iterator(dstPtr), dstEnd);

        for (; srcEndPtr != srcPtr; srcPtr = srcPtr->d_next_p) {
            emplace(dstEnd, MoveUtil::move(srcPtr->d_value));
        }
    }

    return *this;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class VALUE, class ALLOCATOR>
inline
list<VALUE, ALLOCATOR>& list<VALUE, ALLOCATOR>::operator=(
                                              std::initializer_list<VALUE> rhs)
{
    assign(rhs.begin(), rhs.end());
    return *this;
}
#endif

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::assign(size_type numElements, const VALUE& value)
{
    NodePtr              dst_p    = this->headNode();
    const const_iterator dstEnd   = this->cend();
    const NodePtr        dstEnd_p = dstEnd.d_node_p;

    for (; 0 < numElements && dstEnd_p != dst_p;
                                      --numElements, dst_p = dst_p->d_next_p) {
        dst_p->d_value = value;
    }

    erase(const_iterator(dst_p), dstEnd);

    for (; 0 < numElements; --numElements) {
        insert(dstEnd, value);
    }
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::assign(std::initializer_list<VALUE> values)
{
    assign(values.begin(), values.end());
}
#endif

                              // *** iterators ***

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::iterator list<VALUE, ALLOCATOR>::begin()
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return iterator(headNode());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::iterator list<VALUE, ALLOCATOR>::end()
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return iterator(d_sentinel);
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::reverse_iterator
list<VALUE, ALLOCATOR>::rbegin() BSLS_KEYWORD_NOEXCEPT
{
    return reverse_iterator(end());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::reverse_iterator
list<VALUE, ALLOCATOR>::rend() BSLS_KEYWORD_NOEXCEPT
{
    return reverse_iterator(begin());
}

                            // *** modify size ***

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::clear() BSLS_KEYWORD_NOEXCEPT
{
    const NodePtr e = d_sentinel;
    for (NodePtr p = d_sentinel->d_next_p; e != p; ) {
        NodePtr condemned = p;
        p = p->d_next_p;
        deleteNode(condemned);
    }

    linkNodes(d_sentinel, d_sentinel);
    sizeRef() = 0;
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::resize(size_type newSize)
{
    if (newSize > sizeRef()) {
        const_iterator ce = cend();
        do {
            emplace(ce);
        } while (newSize > sizeRef());
    }
    else {
        NodePtr e = d_sentinel;
        NodePtr p = e->d_prev_p;
        for (size_type d = sizeRef() - newSize; d > 0; --d) {
            NodePtr condemned = p;
            p = p->d_prev_p;
            deleteNode(condemned);
        }
        linkNodes(p, e);
        sizeRef() = newSize;
    }
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::resize(size_type newSize, const VALUE& value)
{
    if (newSize > sizeRef()) {
        const_iterator ce = cend();
        do {
            emplace(ce, value);
        } while (newSize > sizeRef());
    }
    else {
        NodePtr e = d_sentinel;
        NodePtr p = e->d_prev_p;
        for (size_type d = sizeRef() - newSize; d > 0; --d) {
            NodePtr condemned = p;
            p = p->d_prev_p;
            deleteNode(condemned);
        }
        linkNodes(p, e);
        sizeRef() = newSize;
    }
}

                                // element access:

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::back()
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    return d_sentinel->d_prev_p->d_value;
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::front()
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    return headNode()->d_value;
}

                                // *** end erase ***

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::pop_back()
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    erase(--cend());
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::pop_front()
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    erase(cbegin());
}

                         // *** random access erase ***

template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::erase(const_iterator position)
{
    BSLS_ASSERT(position.d_node_p != d_sentinel);

    NodePtr  condemned = position.d_node_p;
    iterator ret(condemned->d_next_p);

    linkNodes(condemned->d_prev_p, condemned->d_next_p);
    deleteNode(condemned);
    --sizeRef();
    return ret;
}

template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::erase(const_iterator dstBegin, const_iterator dstEnd)
{
    NodePtr       p = dstBegin.d_node_p;
    const NodePtr e = dstEnd.  d_node_p;

    linkNodes(p->d_prev_p, e);

    size_type numDeleted = 0;
    for (; e != p; ++numDeleted) {
        NodePtr condemned = p;
        p = p->d_next_p;
        deleteNode(condemned);
    }

    sizeRef() -= numDeleted;

    return iterator(e);
}

                            // *** end inserts ***

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class VALUE, class ALLOCATOR>
template <class... ARGS>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_back(ARGS&&... arguments)
{
    emplace(cend(), BSLS_COMPILERFEATURES_FORWARD(ARGS, arguments)...);
    return back();
}
#endif

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class VALUE, class ALLOCATOR>
template <class... ARGS>
inline
typename list<VALUE, ALLOCATOR>::reference
list<VALUE, ALLOCATOR>::emplace_front(ARGS&&... arguments)
{
    emplace(cbegin(), BSLS_COMPILERFEATURES_FORWARD(ARGS, arguments)...);
    return front();
}
#endif

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::push_back(const VALUE& value)
{
    emplace(cend(), value);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::push_back(
                                   BloombergLP::bslmf::MovableRef<VALUE> value)
{
    emplace(cend(), MoveUtil::move(value));
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::push_front(const VALUE& value)
{
    emplace(cbegin(), value);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::push_front(
                                   BloombergLP::bslmf::MovableRef<VALUE> value)
{
    emplace(cbegin(), MoveUtil::move(value));
}

                       // *** random access inserts ***

#if !BSLS_COMPILERFEATURES_SIMULATE_CPP11_FEATURES
template <class VALUE, class ALLOCATOR>
template <class... ARGS>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::emplace(const_iterator position, ARGS&&... arguments)
{
    NodePtr p = allocateNode();
    NodeProctor proctor(this, p);
    AllocTraits::construct(allocatorImp(),
                           BloombergLP::bsls::Util::addressOf(p->d_value),
                           BSLS_COMPILERFEATURES_FORWARD(ARGS, arguments)...);
    proctor.release();
    return insertNode(position, p);
}
#endif

template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::insert(const_iterator dstPosition, const VALUE& value)
{
    return emplace(dstPosition, value);
}

template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::insert(
                             const_iterator                        dstPosition,
                             BloombergLP::bslmf::MovableRef<VALUE> value)
{
    return emplace(dstPosition, MoveUtil::move(value));
}

template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::insert(const_iterator dstPosition,
                               size_type      numElements,
                               const VALUE&   value)
{
    if (0 == numElements) {
        return dstPosition.unconst();                                 // RETURN
    }

    // Remember the position of the first node inserted before 'dstPosition'.

    iterator ret = emplace(dstPosition, value);

    // And put the rest of the nodes after it.

    for (--numElements; numElements > 0; --numElements) {
        emplace(dstPosition, value);
    }

    return ret;
}

#if defined(BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS)
template <class VALUE, class ALLOCATOR>
typename list<VALUE, ALLOCATOR>::iterator
list<VALUE, ALLOCATOR>::insert(const_iterator               dstPosition,
                               std::initializer_list<VALUE> values)
{
    return insert(dstPosition, values.begin(), values.end());
}
#endif

                          // *** list operations ***

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::merge(list& other)
{
    BSLS_ASSERT_SAFE(this->allocatorImp() == other.allocatorImp());

    merge(other, DefaultLessThan());
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::merge(BloombergLP::bslmf::MovableRef<list> other)
{
    list& lvalue = other;

    BSLS_ASSERT_SAFE(this->allocatorImp() == lvalue.allocatorImp());

    merge(lvalue, DefaultLessThan());
}

template <class VALUE, class ALLOCATOR>
template <class COMPARE>
void list<VALUE, ALLOCATOR>::merge(list& other, COMPARE comparator)
{
    if (&other == this) {
        return;                                                       // RETURN
    }

    BSLS_ASSERT(this->allocatorImp() == other.allocatorImp());

    if (other.empty()) {
        // This is an important special case to avoid pointing to sentinel.

        return;                                                       // RETURN
    }

    // Splice 'other' to the end of '*this', but remember the first node of the
    // appended sequence.

    NodePtr xfirst = other.d_sentinel->d_next_p;
    splice(end(), other);

    // Call 'mergeImp' with a pointer to the first node of the original list, a
    // pointer to the first node of 'other' (which also ends the original
    // list), and a pointer to the sentinel (which now ends 'other').

    mergeImp(d_sentinel->d_next_p, xfirst, d_sentinel, comparator);
}

template <class VALUE, class ALLOCATOR>
template <class COMPARE>
inline
void list<VALUE, ALLOCATOR>::merge(
                               BloombergLP::bslmf::MovableRef<list> other,
                               COMPARE                              comparator)
{
    list& lvalue = other;

    BSLS_ASSERT_SAFE(this->allocatorImp() == lvalue.allocatorImp());

    merge(lvalue, comparator);
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::remove(const VALUE& value)
{
    const const_iterator e = cend();
    for (const_iterator i = cbegin(); e != i; ) {
        // Standard says to use 'operator==', not 'std::equal_to'.

        if (value == *i) {
            i = erase(i);
        }
        else {
            ++i;
        }
    }
}

template <class VALUE, class ALLOCATOR>
template <class PREDICATE>
void list<VALUE, ALLOCATOR>::remove_if(PREDICATE predicate)
{
    const iterator e = end();
    for (iterator i = begin(); e != i; ) {
        if (predicate(*i)) {
            i = erase(i);
        }
        else {
            ++i;
        }
    }
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::reverse() BSLS_KEYWORD_NOEXCEPT
{
    NodePtr sentinel = d_sentinel;
    NodePtr p = sentinel;

    do {
        NodePtr tmp = p->d_next_p;
        p->d_next_p = p->d_prev_p;
        p->d_prev_p = tmp;
        p = tmp;
    } while (p != sentinel);
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::sort()
{
    sort(DefaultLessThan());
}

template <class VALUE, class ALLOCATOR>
template <class COMPARE>
void list<VALUE, ALLOCATOR>::sort(COMPARE comparator)
{
    if (sizeRef() < 2) {
        return;                                                       // RETURN
    }
    NodePtr node1 = d_sentinel->d_next_p;
    sortImp(&node1, size(), comparator);
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::splice(const_iterator dstPosition, list& src)
{
    BSLS_ASSERT(allocatorImp() == src.allocatorImp());
    BSLS_ASSERT(&src != this);

    if (src.empty()) {
        return;                                                       // RETURN
    }

    NodePtr   pPos   = dstPosition.d_node_p;
    NodePtr   pFirst = src.headNode();
    NodePtr   pLast  = src.d_sentinel->d_prev_p;
    size_type n      = src.sizeRef();

    // Splice contents out of 'src'.

    linkNodes(src.d_sentinel, src.d_sentinel);
    src.sizeRef() = 0;

    // Splice contents into '*this'.

    linkNodes(pPos->d_prev_p, pFirst);
    linkNodes(pLast,          pPos);
    sizeRef() += n;
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::splice(
                              const_iterator                       dstPosition,
                              BloombergLP::bslmf::MovableRef<list> src)
{
    splice(dstPosition, MoveUtil::access(src));
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::splice(const_iterator dstPosition,
                                    list&          src,
                                    const_iterator srcNode)
{
    BSLS_ASSERT(allocatorImp() == src.allocatorImp());

    NodePtr pPos          = dstPosition.d_node_p;
    NodePtr pSrcNode      = srcNode.d_node_p;
    NodePtr pAfterSrcNode = pSrcNode->d_next_p;

    if (pPos == pSrcNode || pPos == pAfterSrcNode) {
        return;                                                       // RETURN
    }

    // Splice contents out of 'src'.

    linkNodes(pSrcNode->d_prev_p, pAfterSrcNode);
    --src.sizeRef();

    // Splice contents into '*this'.

    linkNodes(pPos->d_prev_p, pSrcNode);
    linkNodes(pSrcNode,       pPos);
    ++sizeRef();
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::splice(
                              const_iterator                       dstPosition,
                              BloombergLP::bslmf::MovableRef<list> src,
                              const_iterator                       srcNode)
{
    splice(dstPosition, MoveUtil::access(src), srcNode);
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::splice(const_iterator dstPosition,
                                    list&          src,
                                    const_iterator first,
                                    const_iterator last)
{
    BSLS_ASSERT(allocatorImp() == src.allocatorImp());

    size_type n = bsl::distance(first, last);

    if (0 == n) {
        return;                                                       // RETURN
    }

    NodePtr pPos     = dstPosition.d_node_p;
    NodePtr pFirst   = first.d_node_p;
    NodePtr pLast    = last.d_node_p;
    NodePtr pSrcLast = pLast->d_prev_p;

    // Splice contents out of 'src'.

    linkNodes(pFirst->d_prev_p, pLast);
    src.sizeRef() -= n;

    // Splice contents into '*this'.

    linkNodes(pPos->d_prev_p, pFirst);
    linkNodes(pSrcLast,       pPos);
    sizeRef() += n;
}

template <class VALUE, class ALLOCATOR>
inline
void list<VALUE, ALLOCATOR>::splice(
                              const_iterator                       dstPosition,
                              BloombergLP::bslmf::MovableRef<list> src,
                              const_iterator                       first,
                              const_iterator                       last)
{
    splice(dstPosition, MoveUtil::access(src), first, last);
}

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::unique()
{
    if (size() < 2) {
        return;                                                       // RETURN
    }

    iterator i = begin();
    iterator e = end();
    while (i != e) {
        reference match = *i++;
        while (i != e && *i == match) {
            i = erase(i);
        }
    }
}

template <class VALUE, class ALLOCATOR>
template <class EQ_PREDICATE>
void list<VALUE, ALLOCATOR>::unique(EQ_PREDICATE binaryPredicate)
{
    if (size() < 2) {
        return;                                                       // RETURN
    }

    iterator i = begin();
    iterator e = end();
    while (i != e) {
        reference match = *i++;
        while (i != e && binaryPredicate(*i, match)) {
            i = erase(i);
        }
    }
}

                              // *** misc ***

template <class VALUE, class ALLOCATOR>
void list<VALUE, ALLOCATOR>::swap(list& other)
    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(AllocTraits::is_always_equal::value)
{
    // C++11 behavior for member 'swap': undefined for unequal allocators.
    // BSLS_ASSERT(allocatorImp() == other.allocatorImp());

    if (AllocTraits::propagate_on_container_swap::value) {
        using std::swap;

        swap(d_sentinel,     other.d_sentinel);
        swap(allocatorImp(), other.allocatorImp());
        swap(sizeRef(),      other.sizeRef());
    }
    else if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(
                                     allocatorImp() == other.allocatorImp())) {
        quickSwap(&other);
    }
    else {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;

        // Create copies using the move constructor, then swap both containers
        // with them.  Note that if no move constructor exists, but a copy
        // constructor does, the copy constructor will be used.

        // Also note that if either of these copies throws, it could leave the
        // two containers in a changed state.  They are, however, guaranteed to
        // be left in valid state.

        list toOtherCopy(MoveUtil::move(*this), other.allocatorImp());
        list toThisCopy( MoveUtil::move(other), this->allocatorImp());

        toOtherCopy.quickSwap(&other);
        toThisCopy .quickSwap(this);
    }
}

// ACCESSORS

                               // *** iterators ***

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_iterator
list<VALUE, ALLOCATOR>::begin() const BSLS_KEYWORD_NOEXCEPT
{
    return const_iterator(headNode());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_iterator
list<VALUE, ALLOCATOR>::end() const BSLS_KEYWORD_NOEXCEPT
{
    return const_iterator(d_sentinel);
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_iterator
list<VALUE, ALLOCATOR>::cbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return begin();
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_iterator
list<VALUE, ALLOCATOR>::cend() const BSLS_KEYWORD_NOEXCEPT
{
    return end();
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reverse_iterator
list<VALUE, ALLOCATOR>::crbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return rbegin();
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reverse_iterator
list<VALUE, ALLOCATOR>::crend() const BSLS_KEYWORD_NOEXCEPT
{
    return rend();
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reverse_iterator
list<VALUE, ALLOCATOR>::rbegin() const BSLS_KEYWORD_NOEXCEPT
{
    return const_reverse_iterator(end());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reverse_iterator
list<VALUE, ALLOCATOR>::rend() const BSLS_KEYWORD_NOEXCEPT
{
    return const_reverse_iterator(begin());
}

                                  // *** size ***

template <class VALUE, class ALLOCATOR>
inline
bool list<VALUE, ALLOCATOR>::empty() const BSLS_KEYWORD_NOEXCEPT
{
    return 0 == sizeRef();
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::size_type
list<VALUE, ALLOCATOR>::max_size() const BSLS_KEYWORD_NOEXCEPT
{
    return AllocTraits::max_size(allocatorImp());
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::size_type list<VALUE, ALLOCATOR>::size() const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return sizeRef();
}

                           // *** element access ***

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reference
list<VALUE, ALLOCATOR>::back() const
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    return d_sentinel->d_prev_p->d_value;
}

template <class VALUE, class ALLOCATOR>
inline
typename list<VALUE, ALLOCATOR>::const_reference
list<VALUE, ALLOCATOR>::front() const
{
    BSLS_ASSERT_SAFE(sizeRef() > 0);

    return headNode()->d_value;
}

                                // *** misc ***

template <class VALUE, class ALLOCATOR>
inline
ALLOCATOR list<VALUE, ALLOCATOR>::get_allocator() const BSLS_KEYWORD_NOEXCEPT
{
    return allocatorImp();
}

}  // close namespace bsl

// FREE OPERATORS
template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator==(const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return BloombergLP::bslalg::RangeCompare::equal(lhs.begin(),
                                                    lhs.end(),
                                                    lhs.size(),
                                                    rhs.begin(),
                                                    rhs.end(),
                                                    rhs.size());
}

template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator!=(const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return ! (lhs == rhs);
}

template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator< (const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return 0 > BloombergLP::bslalg::RangeCompare::lexicographical(lhs.begin(),
                                                                  lhs.end(),
                                                                  lhs.size(),
                                                                  rhs.begin(),
                                                                  rhs.end(),
                                                                  rhs.size());
}

template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator> (const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return rhs < lhs;
}

template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator<=(const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return !(rhs < lhs);
}

template <class VALUE, class ALLOCATOR>
inline
bool bsl::operator>=(const list<VALUE, ALLOCATOR>& lhs,
                     const list<VALUE, ALLOCATOR>& rhs)
{
    return !(lhs < rhs);
}

// FREE FUNCTIONS
template <class VALUE, class ALLOCATOR, class BDE_OTHER_TYPE>
inline typename bsl::list<VALUE, ALLOCATOR>::size_type
bsl::erase(list<VALUE, ALLOCATOR>& l, const BDE_OTHER_TYPE& value)
{
    // We could use the erase/remove idiom here like we do in the other
    // sequence containers, but this is more efficient, since we just unlink
    // and delete nodes from the list.
    typename list<VALUE, ALLOCATOR>::size_type oldSize = l.size();
    for (typename list<VALUE, ALLOCATOR>::iterator it = l.begin();
                                                                it != l.end();)
    {
        if (value == *it) {
            it = l.erase(it);
        }
        else {
            ++it;
        }
    }
    return oldSize - l.size();
}

template <class VALUE, class ALLOCATOR, class PREDICATE>
inline typename bsl::list<VALUE, ALLOCATOR>::size_type
bsl::erase_if(list<VALUE, ALLOCATOR>& l, PREDICATE predicate)
{
    return BloombergLP::bslstl::AlgorithmUtil::containerEraseIf(l, predicate);
}

template <class VALUE, class ALLOCATOR>
inline
void bsl::swap(list<VALUE, ALLOCATOR>& a, list<VALUE, ALLOCATOR>& b)
    BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(BSLS_KEYWORD_NOEXCEPT_OPERATOR(
                                                                    a.swap(b)))
{
    a.swap(b);
}

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

// Type traits for STL *sequence* containers:
//: o A sequence container defines STL iterators.
//: o A sequence container uses 'bslma' allocators if the (template parameter)
//:   type 'ALLOCATOR' is convertible from 'bslma::Allocator*'.

namespace BloombergLP {

namespace bslalg {

template <class VALUE, class ALLOCATOR>
struct HasStlIterators<bsl::list<VALUE, ALLOCATOR> >
    : bsl::true_type
{};

}  // close namespace bslalg

namespace bslma {

template <class VALUE, class ALLOCATOR>
struct UsesBslmaAllocator<bsl::list<VALUE, ALLOCATOR> >
    : bsl::is_convertible<Allocator*, ALLOCATOR>
{};

}  // close namespace bslma

namespace bslmf {

// A list is bitwise movable if its allocator is bitwise movable.

template <class VALUE, class ALLOCATOR>
struct IsBitwiseMoveable<bsl::list<VALUE, ALLOCATOR> >
    : BloombergLP::bslmf::IsBitwiseMoveable<ALLOCATOR>
{};

}  // close namespace bslmf
}  // close enterprise namespace

#endif // End C++11 code

#endif

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