Some of these member functions are not supported on some containers, depending on the underlying data structure. For example:
array.push_front, emplace_front and pop_front are not supported on string, vector and array.size is not supported on forward_list in order to save time and space.operator[] and at are not supported on linked-lists.This table tells you everything.
Every container has its iterator: Container::iterator. e.g. std::vector<int>::iterator, std::forward_list<std::string>::iterator
auto comes to our rescue!c.begin() returns the iterator to the first element of c.
c.end() returns the iterator to the element following the last element of c.
ForwardIterators: supports *it, it->mem, ++it, it++, it1 == it2, it1 != it2
BidirectionalIterator: a ForwardIterator that can be moved in both directions
--it and it--.RandomAccessIterator: a BidirectionalIterator that can be moved to point to any element in constant time.
it + n, n + it, it - n, it += n, it -= n for an integer n.it[n], equivalent to *(it + n).it1 - it2, returns the distance of two iterators.<, <=, >, >=.ForwardIterators: an iterator that can be moved forward.
forward_list<T>::iteratorBidirectionalIterator: a ForwardIterator that can be moved in both directions
list<T>::iteratorRandomAccessIterator: a BidirectionalIterator that can be moved to point to any element in constant time.
string::iterator, vector<T>::iterator, deque<T>::iterator, array<T,N>::iteratorTo know the category of an iterator of a container, consult its type alias member iterator_category.
using vec_iter = std::vector<int>::iterator;
using category = vec_iter::iterator_category;
Put your mouse on category, and the IDE will tell you what it is.
It is one of the following tags: std::forward_iterator_tag, std::bidirectional_iterator_tag, std::random_access_iterator_tag.
Note: Two other categories InputIterator and OutputIterator will be discussed in recitations.
All sequence containers can be constructed in the following ways:
Container c(b, e), where [b, e) is an iterator range.
[b, e).Container c(n, x), where n is a nonnegative integer and x is a value.
n copies of x.Container c(n), where n is a nonnegative integer.
n elements. All elements are value-initialized.string. (Why?)All sequence containers can be constructed in the following ways:
Container c(b, e), where [b, e) is an iterator range.
[b, e).Container c(n, x), where n is a nonnegative integer and x is a value.
n copies of x.Container c(n), where n is a nonnegative integer.
n elements. All elements are value-initialized.string, because it is meaningless to have n value-initializes chars (all of them will be '\0')!Represent a "set":
Sequence containers do not suffice:
vector only supports quick insertion/deletion at the end.You will learn the appropriate data structures in CS101.
std::setDefined in <set>.
std::set<T> is a set whose elements are of type T. operator<(const T, const T) should be supported, because it is usually implemented as Red-black trees.std::set<T, Cmp> is also available. x < y will be replaced with cmp(x, y), where cmp is a function object of type Cmp.std::set<int> s1; // An empty set of ints
std::set<std::string> s2{"hello", "world"}; // A set of strings,
// initialized with two elements
struct Student { std::string name; int id; };
std::set<Student> s3; // No operator< for Student is available.
// This line alone does not cause error, but you cannot
// insert elements into it.
s3.insert(Student{"Alice", 42}); // Error: No operator< available.
std::setDefined in <set>.
std::set<T> is a set whose elements are of type T. operator<(const T, const T) should be supported, because set is usually implemented as Red-black trees.std::set<T, Cmp> is also available. x < y will be replaced with cmp(x, y), where cmp is a function object of type Cmp.struct Student { std::string name; int id; };
struct CmpStudentByName {
bool operator()(const Student &a, const Student &b) const {
return a.name < b.name;
}
};
std::set<Student, CmpStudentByName> students; // OK
students.insert(Student{"Alice", 42}); // OK
std::setConstructors
std::set<Type> s1{a, b, c, ...};
std::set<Type> s2(begin, end); // An iterator range [begin, end)
C++17 CTAD (Class Template Argument Deduction) also applies:
std::set s1{a, b, c, ...}; // Element type is deduced according to the list
std::set s2(begin, end); // Element type is deduced according to
// the type of elements pointed by `begin` and `end`.
Besides, std::set is copy-constructible, copy-assignable, move-constructible and move-assignable, just as the sequence containers we have learned.
std::set does not contain duplicate elements. These constructors will ignore duplicate elements.
std::set: operationsCommon operations: s.empty(), s.size(), s.clear().
Insertion: insert and emplace. Duplicate elements will not be inserted.
s.insert(x), s.insert({a, b, ...}), s.insert(begin, end).std::set s{3, 2, 5, 5, 1}; // {1, 2, 3, 5}. The duplicate 5 is removed.
std::cout << s.size() << std::endl; // 4
s.insert(42); // {1, 2, 3, 5, 42}
s.insert(42); // Nothing is inserted. (No errors.)
int a[]{10, 20, 30};
s.insert(a, a + 3); // An iterator range.
// s now contains {1, 2, 3, 5, 10, 20, 30, 42}.
s.insert({11, 12}); // {1, 2, 3, 5, 10, 11, 12, 20, 30, 42}.
std::set: insertionInsertion: insert and emplace. Duplicate elements will not be inserted.
s.emplace(args...). Forwards the arguments args... to the constructor of the element type, and constructs the element in-place.std::set<std::string> s;
s.emplace(10, 'c'); // inserts a string "cccccccccc"
s.insert(x) and s.emplace(args...) returns std::pair<iterator, bool>:
.first is an iterator pointing to the inserted element, and .second is true..first is an iterator pointing to the element that prevented the insertion, and .second is false.std::set: iteratorss.begin(), s.end(): Begin and off-the-end iterators.
The iterator of std::set is BidirectionalIterator:
*it, it->mem, ++it, it++, --it, it--, it1 == it2, it1 != it2.The elements are in ascending order: The following assertion always succeeds (if both tmp and iter are dereferenceable).
auto tmp = iter;
++iter;
assert(*tmp < *iter);
std::set: iteratorsElements in a set cannot be modified directly: *iter returns a reference-to-const.
std::set: traversalRange-for still works!
std::set<int> s{5, 5, 7, 3, 20, 12, 42};
for (auto x : s)
std::cout << x << ' ';
std::cout << std::endl;
Output: 3, 5, 7, 12, 20, 42 . The elements are in ascending order.
Equivalent way: Use iterators
for (auto it = s.begin(); it != s.end(); ++it)
std::cout << *it << ' ';
std::cout << std::endl;
std::set: deletionDelete elements: erase
s.erase(x), s.erase(pos), s.erase(begin, end), where pos is an iterator pointing to some element in s, and [begin, end) is an iterator range in s.s.erase(x) removes the element that is equivalent to x, if any.
0 or 1, indicating the number of elements removed.std::set<int> s{5, 5, 7, 3, 20, 12, 42};
std::cout << s.erase(42) << std::endl; // 42 is removed. output: 1
// s is now {3, 5, 7, 12, 20}.
s.erase(++++s.begin()); // 7 is removed.
std::set: element lookups.find(x), s.count(x), and some other functions.
s.find(x) returns an iterator pointing to the element equivalent to x (if found), or s.end() (if not found).
std::set<int> s = someValues();
if (s.find(x) != s.end()) // x is found
// ...
std::set: pros and consThe time complexity of insertion, deletion, and lookup of elements in a std::set: logarithmic in the size of the container. (
Elements are sorted automatically.
Fast random access like v[i] is not supported.
Sets based on red-black trees:
std::setstd::multiset: allows duplicate elementsSets based on hash-tables: (since C++11)
std::unordered_set: hash-table version of std::setstd::unordered_multiset: allows duplicate elementsSets based on hash-tables provides (average-case)
Represent a map:
Container<Type>: Type.std::set<Type>: Type.std::map<Key, Value>: defined in <map>
Key is the type of elements in Value is the type of elements in Example: Count the occurrences of strings.
std::map<std::string, int> counter; // maps every string to an integer
std::string word;
while (std::cin >> word)
++counter[word]; // !!
Now for any string str, counter[str] is an integer indicating how many times str has occurred.
std::map: comparison with std::setstd::map<Key, Value> has two template parameters: Key and Value.
Value, it is a std::set<Key>.
operator<(const Key, const Key) is required.std::map<Key, Value> is std::pair<const Key, Value>.
*iter returns std::pair<const Key, Value> &.std::map: comparison with std::setConstructors:
std::map<Key, Value> m{{key1, value1}, {key2, value2}, ...};
std::map<Key, Value> m(begin, end), but the elements should be pairs:
std::vector<std::pair<int, int>> v{{1, 2}, {3, 4}};
std::map<int, int> m(v.begin(), v.end());
Insertion:
m.insert({key, value})m.insert({{key1, value1}, {key2, value2}, ...})m.insert(begin, end)std::map: comparison with std::setDeletion:
m.erase(pos), m.erase(begin, end): same as std::set<T>::erase.m.erase(key): Removes the element whose key is key.Iterators: BidirectionalIterator, pointing to std::pair<const Key, Value>.
std::map<std::string, int> counter = someValues();
for (auto it = counter.begin(); it != counter.end(); ++it)
std::cout << it->first << " occurred " << it->second << " times.\n";
std::map: traversalUse range-for:
for (const auto &kvpair : counter)
std::cout << kvpair.first << " occurred " << kvpair.second << " times.\n";
It's so annoying to deal with the pair stuff...
std::map: traversalUse range-for:
for (const auto &kvpair : counter)
std::cout << kvpair.first << " occurred " << kvpair.second << " times.\n";
It's so annoying to deal with the pair stuff...
C++17 structured binding kills the game!
for (const auto &[word, occ] : counter)
std::cout << word << " occurred " << occ << " times.\n";
(Looks very much like Python unpacking.)
std::map-specific: operator[]m[key] finds the key-value pair whose key is equivalent to key.
{key, Value{}} - the value is value-initialized.std::map<std::string, int> counter;
std::string word;
while (std::cin >> word)
++counter[word]; // If `word` does not exist in `counter`,
// a pair {word, 0} is inserted first.
std::map: element lookupm.find(key), m.count(key), and some other member functions.
Note: m.find(key) does not insert elements. m[key] will insert an element if that key does not exist.
Maps based on red-black trees:
std::mapstd::multimap: allows duplicate keysMaps based on hash-tables: (since C++11)
std::unordered_map: hash-table version of std::mapstd::unordered_multimap: allows duplicate keysMaps based on hash-tables provides (average-case)
Sequence containers
std::vector<T>: dynamic contiguous array (we are quite familiar with)
std::deque<T>: double-ended queue (often pronounced as "deck")
std::array<T, N>: same as T[N], but it is a containerstd::list<T>: doubly-linked list
std::forward_list<T>: singly-linked list
Associative containers
std::set<T>: A finite set T.std::map<Key, Value>: A map Key and Value respectively.std::set and std::map are ordered: T and Key need to have an ordering, either in the form of operator< or some user-supplied comparator.std::unordered_set and std::unordered_map are unordered and hash-based.