Outline
Purpose
Provide an STL-compliant multimap class.
Classes
Canonical header: bsl_map.h
- See also
- bslstl_map, bslstl_multiset
Description
This component defines a single class template, bsl::multimap
, implementing the standard container holding an ordered sequence of key-value pairs (possibly having duplicate keys), and presenting a mapping from the keys (of a template parameter type, KEY
) to their associated values (of another template parameter type, VALUE
).
An instantiation of multimap
is an allocator-aware, value-semantic type whose salient attributes are its size (number of key-value pairs) and the ordered sequence of key-value pairs the multimap contains. If multimap
is instantiated with either a key type or mapped-value type that is not itself value-semantic, then it will not retain all of its value-semantic qualities. In particular, if either the key or value type cannot be tested for equality, then a multimap
containing that type cannot be tested for equality. It is even possible to instantiate multimap
with a key or mapped-value type that does not have a copy-constructor, in which case the multimap
will not be copyable.
A multimap meets the requirements of an associative container with bidirectional iterators in the C++ standard [23.2.4]. The multimap
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 KEY and VALUE
A multimap
is a fully "Value-Semantic Type" (see bsldoc_glossary ) only if the supplied KEY
and VALUE
template parameters are themselves fully value-semantic. It is possible to instantiate a multimap
with KEY
and VALUE
parameter arguments that do 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 multimap
to describe a function's requirements for the KEY
and VALUE
template parameters. These terms are also defined in section [17.6.3.1] of the C++11 standard. Note that, in the context of a multimap
instantiation, the requirements apply specifically to the multimap's entry type, value_type
, which is an alias for 'pair<const KEY, VALUE>'.
Legend
------
'X' - denotes an allocator-aware container type (e.g., 'multimap')
'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 multimap's ALLOCATOR
template parameter determines how that multimap will allocate memory. The multimap
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 multimap
instantiation is bsl::allocator
, then objects of that multimap type will conform to the standard behavior of a bslma
-allocator-enabled type. Such a multimap 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 multimap throughout its lifetime; otherwise, the multimap will use the default allocator installed at the time of the multimap's construction (see bslma_default ). In addition to directly allocating memory from the indicated bslma::Allocator
, a multimap supplies that allocator's address to the constructors of contained objects of the (template parameter) types KEY
and VALUE
, if respectively, the types define the bslma::UsesBslmaAllocator
trait.
Operations
This section describes the run-time complexity of operations on instances of multimap
:
Legend
------
'K' - (template parameter) type 'KEY' of the 'multimap'
'V' - (template parameter) type 'VALUE' of the 'multimap'
'a', 'b' - two distinct objects of type 'multimap<K, V>'
'rv' - modifiable rvalue of type 'multimap<K, V>'
'n', 'm' - number of elements in 'a' and 'b', respectively
'value_type' - 'pair<const K, V>'
'c' - comparator providing an ordering for objects of type 'K'
'al' - STL-style memory allocator
'i1', 'i2' - two iterators defining a sequence of 'value_type' objects
'k' - object of type 'K'
'v' - object of type 'V'
'vt' - object of type 'value_type'
'rvt' - modifiable rvalue of type 'value_type'
'p1', 'p2' - two 'const_iterator's belonging to 'a'
distance(i1,i2) - number of elements in the range '[i1 .. i2)'
+----------------------------------------------------+--------------------+
| Operation | Complexity |
+====================================================+====================+
| multimap<K, V> a; (default construction) | O[1] |
| multimap<K, V> a(al); | |
| multimap<K, V> a(c, al); | |
+----------------------------------------------------+--------------------+
| multimap<K, V> a(rv); (move construction) | O[1] if 'a' and |
| multimap<K, V> a(rv, al); | 'rv' use the same |
| | allocator, |
| | O[n] otherwise |
+----------------------------------------------------+--------------------+
| multimap<K, V> a(b); (copy construction) | O[n] |
| multimap<K, V> a(b, al); | |
+----------------------------------------------------+--------------------+
| multimap<K, V> a(i1, i2); | O[N] if [i1, i2) |
| multimap<K, V> a(i1, i2, al); | is sorted with |
| multimap<K, V> a(i1, i2, c, al); | 'a.value_comp()', |
| | O[N * log(N)] |
| | otherwise, where N |
| | is distance(i1,i2) |
+----------------------------------------------------+--------------------+
| a.~multimap<K, V>(); (destruction) | O[n] |
+----------------------------------------------------+--------------------+
| a = rv; (move assignment) | O[1] if 'a' and |
| | 'rv' use the same |
| | allocator, |
| | O[n] otherwise |
+----------------------------------------------------+--------------------+
| a = b; (copy assignment) | O[n] |
+----------------------------------------------------+--------------------+
| 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] |
+----------------------------------------------------+--------------------+
| get_allocator() | O[1] |
+----------------------------------------------------+--------------------+
| a.insert(vt) | O[log(n)] |
| a.insert(rvt) | |
| a.emplace(Args&&...) | |
+----------------------------------------------------+--------------------+
| a.insert(p1, vt) | amortized constant |
| a.insert(p1, rvt) | if the value is |
| a.emplace(p1, Args&&...) | inserted right |
| | before p1, |
| | O[log(n)] |
| | otherwise |
+----------------------------------------------------+--------------------+
| a.insert(i1, i2) | O[log(N) * |
| | distance(i1,i2)] |
| | |
| | where N is |
| | n + distance(i1,i2)|
+----------------------------------------------------+--------------------+
| a.erase(p1) | amortized constant |
+----------------------------------------------------+--------------------+
| a.erase(k) | O[log(n) + |
| | a.count(k)] |
+----------------------------------------------------+--------------------+
| a.erase(p1, p2) | O[log(n) + |
| | distance(p1, p2)] |
+----------------------------------------------------+--------------------+
| a.erase(p1, p2) | O[log(n) + |
| | distance(p1, p2)] |
+----------------------------------------------------+--------------------+
| a.clear() | O[n] |
+----------------------------------------------------+--------------------+
| a.contains(k) | O[log(n)] |
+----------------------------------------------------+--------------------+
| a.key_comp() | O[1] |
+----------------------------------------------------+--------------------+
| a.value_comp() | O[1] |
+----------------------------------------------------+--------------------+
| a.find(k) | O[log(n)] |
+----------------------------------------------------+--------------------+
| a.count(k) | O[log(n) + |
| | a.count(k)] |
+----------------------------------------------------+--------------------+
| a.lower_bound(k) | O[log(n)] |
+----------------------------------------------------+--------------------+
| a.upper_bound(k) | O[log(n)] |
+----------------------------------------------------+--------------------+
| a.equal_range(k) | O[log(n)] |
+----------------------------------------------------+--------------------+
Usage
In this section we show intended use of this component.
Example 1: Creating a Phone Book
In this example, we will define a class PhoneBook
, that provides a mapping of names to phone numbers. The PhoneBook
class will be implemented using a bsl::multimap
, and will supply manipulators, allowing a client to add or remove entries from the phone book, as well as accessors, allowing clients to efficiently lookup entries by name, and to iterate over the entries in the phone book in sorted order.
Note that this example uses a type string
that is based on the standard type string
(see bslstl_string ). For the sake of brevity, the implementation of string
is not explored here.
First, we define an alias for a pair of string
objects that we will use to represent names in the phone book:
Definition bslstl_pair.h:1210
Then, we define a comparison functor for FirstAndLastName
objects (note that this comparator is required because we intend for the last name to take precedence over the first name in the ordering of entries maintained by the phone book, which differs from the behavior supplied by operator<
for bsl::pair
):
struct FirstAndLastNameLess {
bool operator()(const FirstAndLastName& lhs,
const FirstAndLastName& rhs) const
{
int cmp = std::strcmp(lhs.second.c_str(), rhs.second.c_str());
if (0 == cmp) {
cmp = std::strcmp(lhs.first.c_str(), rhs.first.c_str());
}
return cmp < 0;
}
};
Next, we define the public interface for PhoneBook
:
Here, we create a type alias, NameToNumberMap
, for a bsl::multimap
that will serve as the data member for a PhoneBook
. A NameToNumberMap
has keys of type FirstAndLastName
, mapped-values of type bsls::Types::Uint64
, and a comparator of type FirstAndLastNameLess
. We use the default ALLOCATOR
template parameter as we intend to use PhoneBook
with bslma
style allocators:
FirstAndLastNameLess> NameToNumberMap;
NameToNumberMap d_nameToNumber;
friend bool operator==(const PhoneBook& lhs, const PhoneBook& rhs);
public:
typedef NameToNumberMap::const_iterator ConstIterator;
PhoneBook(const PhoneBook& original,
PhoneBook& operator=(const PhoneBook& rhs);
void addEntry(const FirstAndLastName& name, PhoneNumber number);
int removeEntry(const FirstAndLastName& name, PhoneNumber number);
const FirstAndLastName& name) const;
ConstIterator begin() const;
ConstIterator end() const;
size_t numEntries() const;
};
Definition bslstl_multimap.h:665
Definition bslma_allocator.h:457
unsigned long long Uint64
Definition bsls_types.h:137
Then, we declare the free operators for PhoneBook
:
inline
bool operator==(const PhoneBook& lhs, const PhoneBook& rhs);
inline
bool operator!=(const PhoneBook& lhs, const PhoneBook& rhs);
Now, we define the implementations methods of the PhoneBook
class:
inline
: d_nameToNumber(FirstAndLastNameLess(), basicAllocator)
{
}
Notice that, on construction, we pass the contained bsl::multimap
(d_nameToNumber
), a default constructed FirstAndLastNameLess
object that it will use to perform comparisons, and the allocator supplied to PhoneBook
at construction'.
inline
PhoneBook::PhoneBook(const PhoneBook& original,
: d_nameToNumber(original.d_nameToNumber, basicAllocator)
{
}
inline
PhoneBook& PhoneBook::operator=(const PhoneBook& rhs)
{
d_nameToNumber = rhs.d_nameToNumber;
return *this;
}
inline
void PhoneBook::addEntry(const FirstAndLastName& name, PhoneNumber number)
{
d_nameToNumber.insert(NameToNumberMap::value_type(name, number));
}
inline
int PhoneBook::removeEntry(const FirstAndLastName& name,
PhoneNumber number)
{
d_nameToNumber.equal_range(name);
NameToNumberMap::iterator it = range.
first;
int numRemovedEntries = 0;
if (it->second == number) {
it = d_nameToNumber.erase(it);
++numRemovedEntries;
}
else {
++it;
}
}
return numRemovedEntries;
}
inline
PhoneBook::lookupByName(const FirstAndLastName& name) const
{
return d_nameToNumber.equal_range(name);
}
inline
PhoneBook::ConstIterator PhoneBook::begin() const
{
return d_nameToNumber.begin();
}
inline
PhoneBook::ConstIterator PhoneBook::end() const
{
return d_nameToNumber.end();
}
inline
size_t PhoneBook::numEntries() const
{
return d_nameToNumber.size();
}
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
TYPE first
Definition bslstl_pair.h:605
TYPE second
Definition bslstl_pair.h:908
Finally, we implement the free operators for PhoneBook
:
inline
bool operator==(const PhoneBook& lhs, const PhoneBook& rhs)
{
return lhs.d_nameToNumber == rhs.d_nameToNumber;
}
inline
bool operator!=(const PhoneBook& lhs, const PhoneBook& rhs)
{
return !(lhs == rhs);
}