Outline
Purpose
Provide an STL-compliant vector class.
Classes
Canonical header: bsl_vector.h
- See also
- bslstl_deque
Description
This component defines a single class template, bsl::vector, implementing the standard sequential container, std::vector, holding a dynamic array of values of a template parameter type.
An instantiation of vector is an allocator-aware, value-semantic type whose salient attributes are its size (number of values) and the sequence of values the vector contains. If vector is instantiated with a value type that is not value-semantic, then the vector will not retain all of its value-semantic qualities. In particular, if a value type cannot be tested for equality, then a vector containing objects of that type cannot be tested for equality. It is even possible to instantiate vector with a value type that does not have a copy-constructor, in which case the vector will not be copyable.
A vector meets the requirements of a sequential container with random access iterators in the C++ standard [vector]. The vector 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_TYPE
A vector is a fully Value-Semantic Type (see bsldoc_glossary ) only if the supplied VALUE_TYPE template parameter is fully value-semantic. It is possible to instantiate a vector with a VALUE_TYPE parameter that does not have 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 vector to describe a function's requirements for the VALUE_TYPE template parameter. These terms are also defined in section [17.6.3.1] of the C++11 standard. Note that, in the context of a vector instantiation, the requirements apply specifically to the vector's entry type, value_type, which is an alias for VALUE_TYPE.
Glossary
Legend
------
'X' - denotes an allocator-aware container type (e.g., 'vector')
'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.
Memory Allocation
The type supplied as a vector's ALLOCATOR template parameter determines how that vector will allocate memory. The vector template supports allocators meeting the requirements of the C++03 standard; 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 vector instantiation' is bsl::allocator, then objects of that vector type will conform to the standard behavior of a bslma-allocator-enabled type. Such a vector accepts an optional bslma::Allocator argument at construction. If the address of a bslma::Allocator object is explicitly supplied at construction, it is used to supply memory for the vector throughout its lifetime; otherwise, the vector will use the default allocator installed at the time of the vector's construction (see bslma_default ). In addition to directly allocating memory from the indicated bslma::Allocator, a vector supplies that allocator's address to the constructors of contained objects of the (template parameter) type VALUE_TYPE, if it defines the bslma::UsesBslmaAllocator trait.
Operations
This section describes the run-time complexity of operations on instances of vector:
Legend
------
'V' - (template parameter) 'VALUE_TYPE' of the vector
'a', 'b' - two distinct objects of type 'vector<V>'
'rv' - modifiable rvalue of type 'vector<V>'
'n', 'm' - number of elements in 'a' and 'b', respectively
'k' - non-negative integer
'al' - an STL-style memory allocator
'i1', 'i2' - two iterators defining a sequence of 'V' objects
'il' - object of type 'std::initializer_list<V>'
'lil' - length of 'il'
'vt' - object of type 'VALUE_TYPE'
'rvt' - modifiable rvalue of type 'VALUE_TYPE'
'p1', 'p2' - two 'const' iterators belonging to 'a'
distance(i1,i2) - the number of elements in the range [i1, i2)
|-----------------------------------------+-------------------------------|
| Operation | Complexity |
|=========================================+===============================|
| vector<V> a (default construction) | O[1] |
| vector<V> a(al) | |
|-----------------------------------------+-------------------------------|
| vector<V> a(b) (copy construction) | O[n] |
| vector<V> a(b, al) | |
|-----------------------------------------+-------------------------------|
| vector<V> a(rv) (move construction) | O[1] if 'a' and 'rv' use the |
| vector<V> a(rv, al) | same allocator; O[n] otherwise|
|-----------------------------------------+-------------------------------|
| vector<V> a(k) | O[k] |
| vector<V> a(k, al) | |
| vector<V> a(k, vt) | |
| vector<V> a(k, vt, al) | |
|-----------------------------------------+-------------------------------|
| vector<V> a(i1, i2) | O[distance(i1, i2)] |
| vector<V> a(i1, i2, al) | |
|-----------------------------------------+-------------------------------|
| vector<V> a(il) | O[lil] |
| vector<V> a(il, al) | |
|-----------------------------------------+-------------------------------|
| a.~vector<V>() (destruction) | O[n] |
|-----------------------------------------+-------------------------------|
| a.assign(k, vt) | O[k] |
| a.assign(k, rvt) | |
|-----------------------------------------+-------------------------------|
| a.assign(i1, i2) | O[distance(i1, i2)] |
|-----------------------------------------+-------------------------------|
| a.assign(il) | O[lil] |
|-----------------------------------------+-------------------------------|
| get_allocator() | O[1] |
|-----------------------------------------+-------------------------------|
| a.begin(), a.end(), | O[1] |
| a.cbegin(), a.cend(), | |
| a.rbegin(), a.rend(), | |
| a.crbegin(), a.crend() | |
|-----------------------------------------+-------------------------------|
| a.size() | O[1] |
|-----------------------------------------+-------------------------------|
| a.max_size() | O[1] |
|-----------------------------------------+-------------------------------|
| a.resize(k) | O[k] |
| a.resize(k, vt) | |
|-----------------------------------------+-------------------------------|
| a.empty() | O[1] |
|-----------------------------------------+-------------------------------|
| a.reserve(k) | O[k] |
|-----------------------------------------+-------------------------------|
| a.shrink_to_fit() | O[n] |
|-----------------------------------------+-------------------------------|
| a[k] | O[1] |
|-----------------------------------------+-------------------------------|
| a.at(k) | O[1] |
|-----------------------------------------+-------------------------------|
| a.front() | O[1] |
|-----------------------------------------+-------------------------------|
| a.back() | O[1] |
|-----------------------------------------+-------------------------------|
| a.push_back(vt) | O[1] |
| a.push_back(rvt) | |
|-----------------------------------------+-------------------------------|
| a.pop_back() | O[1] |
|-----------------------------------------+-------------------------------|
| a.emplace_back(args) | O[1] |
|-----------------------------------------+-------------------------------|
| a.emplace(p1, args) | O[1 + distance(p1, a.end())] |
|-----------------------------------------+-------------------------------|
| a.insert(p1, vt) | O[1 + distance(p1, a.end())] |
| a.insert(p1, rvt) | |
|-----------------------------------------+-------------------------------|
| a.insert(p1, k, vt) | O[k + distance(p1, a.end())] |
| a.insert(p1, k, rvt) | |
|-----------------------------------------+-------------------------------|
| a.insert(p1, i1, i2) | O[distance(i1, i2) |
| | + distance(p1, a.end())] |
|-----------------------------------------+-------------------------------|
| a.insert(p1, il) | O[lil |
| | + distance(p1, a.end())] |
|-----------------------------------------+-------------------------------|
| a.erase(p1) | O[1 + distance(p1, a.end())] |
|-----------------------------------------+-------------------------------|
| a.erase(p1, p2) | O[distance(p1, p2) |
| | + distance(p1, a.end())] |
|-----------------------------------------+-------------------------------|
| a.swap(b), swap(a, b) | O[1] if 'a' and 'b' use the |
| | same allocator; O[n + m] |
| | otherwise |
|-----------------------------------------+-------------------------------|
| a.clear() | O[n] |
|-----------------------------------------+-------------------------------|
| a = b; (copy assignment) | O[n] |
|-----------------------------------------+-------------------------------|
| a = rv; (move assignment) | O[1] if 'a' and 'rv' use the |
| | same allocator; O[n] otherwise|
|-----------------------------------------+-------------------------------|
| a = il; | O[lil] |
|-----------------------------------------+-------------------------------|
| a == b, a != b | O[n] |
|-----------------------------------------+-------------------------------|
| a < b, a <= b, a > b, a >= b | O[n] |
|-----------------------------------------+-------------------------------|
Comparing a vector of floating point values
The comparison operator performs a bit-wise comparison for floating point types (float and double), which produces results for NaN, +0, and -0 values that do not meet the guarantees provided by the standard. The bslmf::IsBitwiseEqualityComparable trait for double and float types returns true which is incorrect because a comparison with a NaN value is always false, and -0 and +0 are equal.
v.
push_back(bsl::numeric_limits<double>::quiet_NaN());
ASSERT(v == v);
Definition bslstl_vector.h:1025
void push_back(const VALUE_TYPE &value)
Definition bslstl_vector.h:3760
Addressing this issue, i.e., updating bslmf::IsBitwiseEqualityComparable to return false for floating point types, could potentially destabilize production software so the change (for the moment) has not been made.
Usage
In this section we show intended use of this component.
Example 1: Creating a Matrix Type
Suppose we want to define a value-semantic type representing a dynamically resizable two-dimensional matrix.
First, we define the public interface for the MyMatrix class template:
template <class TYPE>
class MyMatrix {
public:
Here, we create a type alias, RowType, for an instantiation of bsl::vector to represent a row of TYPE objects in the matrix. We create another type alias, MatrixType, for an instantiation of bsl::vector to represent the entire matrix of TYPE objects as a list of rows:
private:
MatrixType d_matrix;
int d_numColumns;
template <class T>
friend bool operator==(const MyMatrix<T>&, const MyMatrix<T>&);
public:
typedef typename MatrixType::const_iterator ConstRowIterator;
MyMatrix(int numRows,
int numColumns,
MyMatrix(const MyMatrix& original,
MyMatrix& operator=(const MyMatrix& rhs);
void clear();
void insertColumn(int columnIndex);
void insertRow(int rowIndex);
TYPE& theModifiableValue(int rowIndex, int columnIndex);
int numRows() const;
int numColumns() const;
ConstRowIterator beginRow() const;
ConstRowIterator endRow() const;
const TYPE& theValue(int rowIndex, int columnIndex) const;
};
Definition bslma_allocator.h:457
Then we declare the free operator for MyMatrix:
template <class TYPE>
MyMatrix<TYPE> operator==(const MyMatrix<TYPE>& lhs,
const MyMatrix<TYPE>& rhs);
template <class TYPE>
MyMatrix<TYPE> operator!=(const MyMatrix<TYPE>& lhs,
const MyMatrix<TYPE>& rhs);
template <class TYPE>
MyMatrix<TYPE> operator*(const MyMatrix<TYPE>& lhs,
const MyMatrix<TYPE>& rhs);
Now, we define the methods of MyMatrix:
template <class TYPE>
MyMatrix<TYPE>::MyMatrix(int numRows,
int numColumns,
: d_matrix(numRows, basicAllocator)
, d_numColumns(numColumns)
{
for (typename MatrixType::iterator itr = d_matrix.begin();
itr != d_matrix.end();
++itr) {
itr->resize(d_numColumns);
}
}
template <class TYPE>
MyMatrix<TYPE>::MyMatrix(const MyMatrix& original,
: d_matrix(original.d_matrix, basicAllocator)
, d_numColumns(original.d_numColumns)
{
}
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
Notice that we pass the contained bsl::vector (d_matrix) the allocator specified at construction to supply memory. If the (template parameter) TYPE of the elements has the bslalg_TypeTraitUsesBslmaAllocator trait, this allocator will be passed by the vector to the elements as well.
template <class TYPE>
MyMatrix<TYPE>& MyMatrix<TYPE>::operator=(const MyMatrix& rhs)
{
d_matrix = rhs.d_matrix;
d_numColumns = rhs.d_numColumns;
}
template <class TYPE>
void MyMatrix<TYPE>::clear()
{
d_matrix.clear();
d_numColumns = 0;
}
template <class TYPE>
void MyMatrix<TYPE>::insertColumn(int colIndex) {
for (typename MatrixType::iterator itr = d_matrix.begin();
itr != d_matrix.end();
++itr) {
itr->insert(itr->begin() + colIndex, TYPE());
}
++d_numColumns;
}
template <class TYPE>
void MyMatrix<TYPE>::insertRow(int rowIndex)
{
typename MatrixType::iterator itr =
d_matrix.insert(d_matrix.begin() + rowIndex, RowType());
itr->resize(d_numColumns);
}
template <class TYPE>
TYPE& MyMatrix<TYPE>::theModifiableValue(int rowIndex, int columnIndex)
{
return d_matrix[rowIndex][columnIndex];
}
template <class TYPE>
int MyMatrix<TYPE>::numRows() const
{
return d_matrix.size();
}
template <class TYPE>
int MyMatrix<TYPE>::numColumns() const
{
return d_numColumns;
}
template <class TYPE>
typename MyMatrix<TYPE>::ConstRowIterator MyMatrix<TYPE>::beginRow() const
{
return d_matrix.begin();
}
template <class TYPE>
typename MyMatrix<TYPE>::ConstRowIterator MyMatrix<TYPE>::endRow() const
{
return d_matrix.end();
}
template <class TYPE>
const TYPE& MyMatrix<TYPE>::theValue(int rowIndex, int columnIndex) const
{
return d_matrix[rowIndex][columnIndex];
}
Finally, we defines the free operators for MyMatrix:
template <class TYPE>
MyMatrix<TYPE> operator==(const MyMatrix<TYPE>& lhs,
const MyMatrix<TYPE>& rhs)
{
return lhs.d_numColumns == rhs.d_numColumns &&
lhs.d_matrix == rhs.d_matrix;
}
template <class TYPE>
MyMatrix<TYPE> operator!=(const MyMatrix<TYPE>& lhs,
const MyMatrix<TYPE>& rhs)
{
return !(lhs == rhs);
}