Outline
Purpose
Provide an STL-compliant list class.
Classes
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
comp(a, b)
&& comp(b, c)
implies comp(a, c)
comp(a, b)
implies !comp(b, a)
!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] |
+----------------------------------------------------+--------------------+
void swap(OptionValue &a, OptionValue &b)
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>
class Star
{
double d_x, d_y;
int d_brightness;
public:
Star()
: d_x(0), d_y(0), d_brightness(0)
{
}
Star(double x, double y, int b)
: d_x(x), d_y(y), d_brightness(b)
{
}
bool read(FILE *input);
void write(FILE *output) const;
double x() const
{
return d_x;
}
double y() const
{
return d_y;
}
int brightness() const
{
return d_brightness;
}
};
bool operator==(
const Star& lhs,
const Star& rhs);
bool operator!=(
const Star& lhs,
const Star& rhs);
bool operator< (const Star& lhs, const Star& rhs);
bool operator!=(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
bool operator==(const FileCleanerConfiguration &lhs, const FileCleanerConfiguration &rhs)
Definition bdldfp_decimal.h:5188
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);
}
else {
++i;
}
}
}
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");
assert(input);
list<Star> starList;
assert(starList.empty());
readData(&starList, input);
assert(10 == starList.size());
fclose(input);
filter(&starList);
assert(4 == starList.size());
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");
assert(input);
list<Star> starList1;
assert(starList1.empty());
readData(&starList1, input);
assert(10 == starList1.size());
fclose(input);
input = fopen("star_data2.txt", "r");
assert(input);
list<Star> starList2;
assert(starList2.empty());
readData(&starList2, input);
assert(8 == starList2.size());
fclose(input);
filter(&starList1);
assert(4 == starList1.size());
filter(&starList2);
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);
list<Star> tmp2(starList2);
tmp1.insert(tmp1.end(), tmp2.begin(), tmp2.end());
assert(7 == tmp1.size());
assert(3 == tmp2.size());
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());
assert(0 == tmp2.size());
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:
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);
assert(7 == starList1.size());
assert(0 == starList2.size());
Now, since the two star surveys may overlap, we want to eliminate duplicates. We accomplish this by using the unique
member function:
starList1.unique();
assert(6 == starList1.size());
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);
}
{
return lhs.x() == rhs.x()
&& lhs.y() == rhs.y()
&& lhs.brightness() == rhs.brightness();
}
{
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();
}
bool operator<(const MetricId &lhs, const MetricId &rhs)