JTC1/SC22/WG21
N4161
Document number: N4161
Date: 2014-10-09
Project: Programming Language C++, Library Working Group
Reply-to: Stephan T. Lavavej <stl@microsoft.com>
Uniform Container Erasure (Revision 1)
I. Introduction
This is a proposal to add erase_if(container, pred) and
erase(container, value), making it easier to eliminate unwanted elements
correctly and efficiently. This updates N4009 (see [1]) which proposed
erase_if(), by adding erase() and answering additional questions. Please see
the original proposal for the rationale behind this feature, which is not
repeated here.
II. Standardese
1. In 1.3 [general.namespaces] Table 1 "C++ library headers", add:
<experimental/deque>
<experimental/forward_list>
<experimental/list>
<experimental/map>
<experimental/set>
<experimental/string>
<experimental/unordered_map>
<experimental/unordered_set>
<experimental/vector>
2. In 1.5 [general.feature.test] Table 2 "Significant features in this
technical specification", add:
Doc. No.: N4161
Title: Uniform Container Erasure
Primary Section: [container.erasure]
Macro Name Suffix: erase_if
Value: 201410
Header: <experimental/vector>
3. Add a new section "Uniform container erasure" [container.erasure]
containing the following new sections.
4. Add a new section "Header synopsis" [container.erasure.syn] containing:
For brevity, this section specifies the contents of 9 headers, each of which
behaves as described by [general.namespaces].
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// [container.erasure.erase_if], function template erase_if
// [container.erasure.erase], function template erase
// <experimental/string>
template <class charT, class traits, class A, class Predicate>
void erase_if(basic_string<charT, traits, A>& c, Predicate pred);
template <class charT, class traits, class A, class U>
void erase(basic_string<charT, traits, A>& c, const U& value);
// <experimental/deque>
template <class T, class A, class Predicate>
void erase_if(deque<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(deque<T, A>& c, const U& value);
// <experimental/vector>
template <class T, class A, class Predicate>
void erase_if(vector<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(vector<T, A>& c, const U& value);
// <experimental/forward_list>
template <class T, class A, class Predicate>
void erase_if(forward_list<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(forward_list<T, A>& c, const U& value);
// <experimental/list>
template <class T, class A, class Predicate>
void erase_if(list<T, A>& c, Predicate pred);
template <class T, class A, class U>
void erase(list<T, A>& c, const U& value);
// <experimental/map>
template <class K, class T, class C, class A, class Predicate>
void erase_if(map<K, T, C, A>& c, Predicate pred);
template <class K, class T, class C, class A, class Predicate>
void erase_if(multimap<K, T, C, A>& c, Predicate pred);
template <class K, class T, class C, class A, class U>
void erase(map<K, T, C, A>& c, const U& value);
template <class K, class T, class C, class A, class U>
void erase(multimap<K, T, C, A>& c, const U& value);
// <experimental/set>
template <class K, class C, class A, class Predicate>
void erase_if(set<K, C, A>& c, Predicate pred);
template <class K, class C, class A, class Predicate>
void erase_if(multiset<K, C, A>& c, Predicate pred);
template <class K, class C, class A, class U>
void erase(set<K, C, A>& c, const U& value);
template <class K, class C, class A, class U>
void erase(multiset<K, C, A>& c, const U& value);
// <experimental/unordered_map>
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_map<K, T, H, P, A>& c, const U& value);
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_multimap<K, T, H, P, A>& c, const U& value);
// <experimental/unordered_set>
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
template <class K, class H, class P, class A, class U>
void erase(unordered_set<K, H, P, A>& c, const U& value);
template <class K, class H, class P, class A, class U>
void erase(unordered_multiset<K, H, P, A>& c, const U& value);
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
5. Add a new section "Function template erase_if" [container.erasure.erase_if]
containing:
template <class charT, class traits, class A, class Predicate>
void erase_if(basic_string<charT, traits, A>& c, Predicate pred);
template <class T, class A, class Predicate>
void erase_if(deque<T, A>& c, Predicate pred);
template <class T, class A, class Predicate>
void erase_if(vector<T, A>& c, Predicate pred);
Effects: Equivalent to:
c.erase(remove_if(c.begin(), c.end(), pred), c.end());
template <class T, class A, class Predicate>
void erase_if(forward_list<T, A>& c, Predicate pred);
template <class T, class A, class Predicate>
void erase_if(list<T, A>& c, Predicate pred);
Effects: Equivalent to:
c.remove_if(pred);
template <class K, class T, class C, class A, class Predicate>
void erase_if(map<K, T, C, A>& c, Predicate pred);
template <class K, class T, class C, class A, class Predicate>
void erase_if(multimap<K, T, C, A>& c, Predicate pred);
template <class K, class C, class A, class Predicate>
void erase_if(set<K, C, A>& c, Predicate pred);
template <class K, class C, class A, class Predicate>
void erase_if(multiset<K, C, A>& c, Predicate pred);
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);
Effects: Equivalent to:
for (auto i = c.begin(), last = c.end(); i != last; ) {
if (pred(*i)) {
i = c.erase(i);
} else {
++i;
}
}
6. Add a new section "Function template erase" [container.erasure.erase]
containing:
template <class charT, class traits, class A, class U>
void erase(basic_string<charT, traits, A>& c, const U& value);
template <class T, class A, class U>
void erase(deque<T, A>& c, const U& value);
template <class T, class A, class U>
void erase(vector<T, A>& c, const U& value);
Effects: Equivalent to:
c.erase(remove(c.begin(), c.end(), value), c.end());
template <class T, class A, class U>
void erase(forward_list<T, A>& c, const U& value);
template <class T, class A, class U>
void erase(list<T, A>& c, const U& value);
template <class K, class T, class C, class A, class U>
void erase(map<K, T, C, A>& c, const U& value);
template <class K, class T, class C, class A, class U>
void erase(multimap<K, T, C, A>& c, const U& value);
template <class K, class C, class A, class U>
void erase(set<K, C, A>& c, const U& value);
template <class K, class C, class A, class U>
void erase(multiset<K, C, A>& c, const U& value);
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_map<K, T, H, P, A>& c, const U& value);
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_multimap<K, T, H, P, A>& c, const U& value);
template <class K, class H, class P, class A, class U>
void erase(unordered_set<K, H, P, A>& c, const U& value);
template <class K, class H, class P, class A, class U>
void erase(unordered_multiset<K, H, P, A>& c, const U& value);
Effects: Equivalent to:
erase_if(c, [&](auto& elem) { return elem == value; });
III. Questions And Answers
Q12. What's new in this revision?
A12. Three things:
* The machinery has moved from std to std::experimental::fundamentals_v2.
* The Standardese has been dramatically condensed.
* erase(container, value) has been added, as requested by the LEWG.
Q13. What about the naming?
A13. I was originally worried about creating confusion between non-member
erase() and member erase(), so I was planning to use Tony Van Eerd's suggestion
of erase_each() and erase_each_if(). But then Hong Hong pointed out something
that made me reconsider my original worries: find(). Specifically:
* Non-member find() is linear-time, uses operator==(), and accepts
heterogeneous types.
* set<K>::find() is log-time, uses operator<(), and is homogeneous (except when
C++14 heterogeneous associative lookup has been explicitly requested
with less<>).
* unordered_set<K>::find() is hash-time, uses hash<K> and operator==(),
and is homogeneous.
Despite the identical naming, this varying behavior isn't widely considered to
be confusing. I now believe that having both non-member erase() and member
erase() won't cause major confusion, especially because I believe that
erase_if() will be used more frequently than non-member erase(). Any minor
confusion will be outweighed by the simplicity of the naming.
Q14. Can you explain how erase(container, value) has been implemented?
A14. These one-liners contain some subtleties. There are three families for
erase_if() (vector-like, list-like, and map-like), but only two implementations
for erase(). Here's why:
* For the vector-like family, std::remove() works (as it accepts heterogeneous
types), and it's shorter than calling erase_if() with a lambda. Note that
for vector<bool>, std::remove() still works, while the lambda depicted here
wouldn't. For simplicity, I've chosen to specify the whole vector-like
family as using std::remove(), as opposed to specifying basic_string and
deque with the lambda, or making the lambda more complicated (with auto&&).
* For the list-like family, member remove() can't be used because it's
homogeneous - the signature is list<T>::remove(const T&). Therefore I'm
calling erase_if() with a lambda.
* For the map-like family, calling erase_if() with a lambda is simpler than
spamming out another erase_nodes() implementation. (Note that if we
spammed out such an implementation, we could use it for the map-like family
and list, but not forward_list which has erase_after().)
The lambda [&](auto& elem) { return elem == value; } has been
written carefully:
* [&] avoids copying.
* (auto& elem) avoids adding constness. (While tempting, std::remove()
doesn't do that, and it's observable to user-defined operator==()
implementations.) This lambda isn't used for vector<bool>, so all of the
other containers will give modifiable lvalues to the lambda. This can be
thought of as hardcoded perfect forwarding.
* "elem == value" matches 25.3.8 [alg.remove]/2's ordering of "*i == value".
Q15. You've provided an implementation, but is there any similar
existing practice?
A15. Boost.Range (see [2]) provides remove_erase(container, value) and
remove_erase_if(container, pred), which differ in:
* Naming: remove_erase[_if]() refers to the erase-remove idiom, while
erase[_if]() doesn't.
* Behavior: remove_erase[_if]() always uses the erase-remove idiom, which is
suboptimal for list (see [1]) and won't compile for forward_list (which has
erase_after()) and the map-like family (which has immutable keys).
erase[_if]() handles the three families optimally.
* Return type: remove_erase[_if]() returns Container&, while erase[_if]()
returns void. (See the next question.)
Boost.PtrContainer (see [3]) provides erase_if(pred) and
erase_if(first, last, pred) as members returning void.
(See Q17 about sub-ranges and Q18 about members.)
Q16. Is it desirable for erase[_if]() to return void?
A16. Yes. Note that list::remove[_if]() also returns void.
Returning an iterator wouldn't be useful. c.erase(iter) and
c.erase(first, last) return an iterator in order to report where the following
elements end up. But after erase(c, value) and erase_if(c, pred), there's no
positioning information to report. Any unwanted elements are gone, leaving
only wanted elements, and the container always knows its own begin() and end().
Returning the number of erased elements wouldn't be especially useful.
While associative c.erase(key) does this, all containers now have constant-time
size() (except forward_list, the world's least useful container), so the number
of erased elements can easily be determined by comparing size() before
and after.
Returning a reference to the container, like Boost's remove_erase[_if]() does,
would permit function call chaining. However, the Standard currently lacks any
other container-based or range-based algorithms. Even when such algorithms are
present, it's far from clear that chaining *mutating* operations would be
desirable. (Applying a series of non-mutating views to a range, like a
reversal followed by a filter, would be less troublesome.) And we certainly
don't want to encourage any repeated calls to erase[_if](). For example,
erase_if(animals, is_bear_or_dog) is far more efficient than
erase_if(animals, is_bear) followed by erase_if(animals, is_dog).
Finally, returning void keeps our options open. If we later decide that
returning the number of erased elements, or a reference to the container,
would in fact be very useful, we can always change a void return type
to non-void. Shipping a non-void return type in Library Fundamentals v2
would make it much harder to change in the future.
Q17. Should erase[_if]() optionally take iterators, in order to allow unwanted
elements to be erased from a sub-range of a container?
A17. No. That is an extremely rare scenario. The Standard doesn't have to
(and shouldn't!) provide helpers for every conceivable task, only the most
important and common ones.
Q18. What about members versus non-members?
A18. The LEWG was split on this issue, slightly favoring non-members.
Q19. Do you want to build a snowman? It doesn't have to be a snowman.
A19. Go away, Anna.
IV. Acknowledgements
Thanks to Eric Niebler and Thorsten Ottosen for explaining Boost's behavior,
to Hong Hong and Tony Van Eerd for their naming suggestions, to Oleg Smolsky
for his questions, and to Billy O'Neal and Giovanni Dicanio for reviewing
this proposal.
V. References
All of the Standardese citations in this proposal are to Working Paper N3936:
http://www.open-std.org/jtc1/sc22/wg21/prot/14882fdis/n3936.pdf
All of the Library Fundamentals v2 citations in this proposal are to
Working Draft N4084:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4084.html
[1] N4009 "Uniform Container Erasure" by Stephan T. Lavavej:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4009.htm
[2] Boost.Range 1.56.0 documentation:
http://www.boost.org/doc/libs/1_56_0/libs/range/doc/html/range/reference/algorithms/new.html
[3] Boost.PtrContainer 1.56.0 documentation:
http://www.boost.org/doc/libs/1_56_0/libs/ptr_container/doc/ptr_sequence_adapter.html#algorithms
VI. Implementation
Compiled with Visual C++ 14 (current development build, after CTP3).
C:\Temp>type meow.cpp
#include <algorithm>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace std {
namespace experimental {
inline namespace fundamentals_v2 {
// production code would be _Ugly and would avoid ADL
template <class charT, class traits, class A, class Predicate>
void erase_if(basic_string<charT, traits, A>& c, Predicate pred) {
c.erase(remove_if(c.begin(), c.end(), pred), c.end());
}
template <class T, class A, class Predicate>
void erase_if(deque<T, A>& c, Predicate pred) {
c.erase(remove_if(c.begin(), c.end(), pred), c.end());
}
template <class T, class A, class Predicate>
void erase_if(forward_list<T, A>& c, Predicate pred) {
c.remove_if(pred);
}
template <class T, class A, class Predicate>
void erase_if(list<T, A>& c, Predicate pred) {
c.remove_if(pred);
}
template <class T, class A, class Predicate>
void erase_if(vector<T, A>& c, Predicate pred) {
c.erase(remove_if(c.begin(), c.end(), pred), c.end());
}
template <class Container, class Predicate>
void erase_nodes_if(Container& c, Predicate pred) { // exposition only
for (auto i = c.begin(), last = c.end(); i != last; ) {
if (pred(*i)) {
i = c.erase(i);
} else {
++i;
}
}
}
template <class K, class T, class C, class A, class Predicate>
void erase_if(map<K, T, C, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class T, class C, class A, class Predicate>
void erase_if(multimap<K, T, C, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class C, class A, class Predicate>
void erase_if(set<K, C, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class C, class A, class Predicate>
void erase_if(multiset<K, C, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class T, class H, class P, class A, class Predicate>
void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_set<K, H, P, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class K, class H, class P, class A, class Predicate>
void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred) {
erase_nodes_if(c, pred);
}
template <class charT, class traits, class A, class U>
void erase(basic_string<charT, traits, A>& c, const U& value) {
c.erase(remove(c.begin(), c.end(), value), c.end());
}
template <class T, class A, class U>
void erase(deque<T, A>& c, const U& value) {
c.erase(remove(c.begin(), c.end(), value), c.end());
}
template <class T, class A, class U>
void erase(forward_list<T, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class T, class A, class U>
void erase(list<T, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class T, class A, class U>
void erase(vector<T, A>& c, const U& value) {
c.erase(remove(c.begin(), c.end(), value), c.end());
}
template <class K, class T, class C, class A, class U>
void erase(map<K, T, C, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class T, class C, class A, class U>
void erase(multimap<K, T, C, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class C, class A, class U>
void erase(set<K, C, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class C, class A, class U>
void erase(multiset<K, C, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_map<K, T, H, P, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class T, class H, class P, class A, class U>
void erase(unordered_multimap<K, T, H, P, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class H, class P, class A, class U>
void erase(unordered_set<K, H, P, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
template <class K, class H, class P, class A, class U>
void erase(unordered_multiset<K, H, P, A>& c, const U& value) {
erase_if(c, [&](auto& elem) { return elem == value; });
}
} // inline namespace fundamentals_v2
} // namespace experimental
} // namespace std
#include <iostream>
#include <utility>
using namespace std;
using namespace std::experimental;
template <typename T> void print_elem(const T& t) {
cout << t;
}
template <typename A, typename B> void print_elem(const pair<A, B>& p) {
cout << p.first << "/" << p.second;
}
template <typename C> void print(const string& s, const C& c) {
cout << s << ": [";
for (auto first = c.begin(), last = c.end(), i = first; i != last; ++i) {
if (i != first) {
cout << " ";
}
print_elem(*i);
}
cout << "]" << endl;
}
int main() {
auto is_vowel = [](const char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
};
auto is_odd = [](const int i) { return i % 2 != 0; };
auto is_odd_pair = [](const pair<const int, string>& p) {
return p.first % 2 != 0;
};
string str("cute fluffy kittens");
cout << "string before: " << str << endl;
erase_if(str, is_vowel);
cout << "string after: " << str << endl;
deque<int> d = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("deque before", d);
erase_if(d, is_odd);
print("deque after", d);
forward_list<int> fl = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("forward_list before", fl);
erase_if(fl, is_odd);
print("forward_list after", fl);
list<int> l = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("list before", l);
erase_if(l, is_odd);
print("list after", l);
vector<int> v = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("vector before", v);
erase_if(v, is_odd);
print("vector after", v);
map<int, string> m = { { 10, "A" }, { 11, "B" }, { 12, "C" },
{ 14, "D" }, { 15, "E" }, { 17, "F" }, { 18, "G" }, { 19, "H" } };
print("map before", m);
erase_if(m, is_odd_pair);
print("map after", m);
multimap<int, string> mm = { { 20, "S" }, { 21, "T" }, { 22, "U" },
{ 22, "V" }, { 23, "W" }, { 23, "X" }, { 24, "Y" }, { 25, "Z" } };
print("multimap before", mm);
erase_if(mm, is_odd_pair);
print("multimap after", mm);
set<int> s = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("set before", s);
erase_if(s, is_odd);
print("set after", s);
multiset<int> ms = { 20, 21, 22, 22, 23, 23, 24, 25 };
print("multiset before", ms);
erase_if(ms, is_odd);
print("multiset after", ms);
unordered_map<int, string> um = { { 10, "A" }, { 11, "B" }, { 12, "C" },
{ 14, "D" }, { 15, "E" }, { 17, "F" }, { 18, "G" }, { 19, "H" } };
print("unordered_map before", um);
erase_if(um, is_odd_pair);
print("unordered_map after", um);
unordered_multimap<int, string> umm = { { 20, "S" }, { 21, "T" },
{ 22, "U" }, { 22, "V" }, { 23, "W" }, { 23, "X" }, { 24, "Y" },
{ 25, "Z" } };
print("unordered_multimap before", umm);
erase_if(umm, is_odd_pair);
print("unordered_multimap after", umm);
unordered_set<int> us = { 10, 11, 12, 14, 15, 17, 18, 19 };
print("unordered_set before", us);
erase_if(us, is_odd);
print("unordered_set after", us);
unordered_multiset<int> ums = { 20, 21, 22, 22, 23, 23, 24, 25 };
print("unordered_multiset before", ums);
erase_if(ums, is_odd);
print("unordered_multiset after", ums);
v = { 17, 29, 1729 };
print("vector before erasing everything", v);
erase_if(v, is_odd);
print("vector after erasing everything", v);
v = { 256, 512, 1024 };
print("vector before erasing nothing", v);
erase_if(v, is_odd);
print("vector after erasing nothing", v);
v.clear();
print("empty vector before", v);
erase_if(v, is_odd);
print("empty vector after", v);
l = { 17, 29, 1729 };
print("list before erasing everything", l);
erase_if(l, is_odd);
print("list after erasing everything", l);
l = { 256, 512, 1024 };
print("list before erasing nothing", l);
erase_if(l, is_odd);
print("list after erasing nothing", l);
l.clear();
print("empty list before", l);
erase_if(l, is_odd);
print("empty list after", l);
s = { 17, 29, 1729 };
print("set before erasing everything", s);
erase_if(s, is_odd);
print("set after erasing everything", s);
s = { 256, 512, 1024 };
print("set before erasing nothing", s);
erase_if(s, is_odd);
print("set after erasing nothing", s);
s.clear();
print("empty set before", s);
erase_if(s, is_odd);
print("empty set after", s);
cout << boolalpha;
auto is_true = [](const bool b) { return b; };
vector<bool> vb = { false, true, false, false, true, true, false, true };
print("vector<bool> before", vb);
erase_if(vb, is_true);
print("vector<bool> after", vb);
v = { 0, 11, 0, 0, 22, 33, 0, 0, 44, 0 };
print("vector before erasing 0", v);
erase(v, 0);
print("vector after erasing 0", v);
l = { 0, 11, 0, 0, 22, 33, 0, 0, 44, 0 };
print("list before erasing 0", l);
erase(l, 0);
print("list after erasing 0", l);
s = { 0, 11, 22, 33, 44 };
print("set before erasing 0", s);
erase(s, 0);
print("set after erasing 0", s);
vb = { false, true, false, false, true, true, false, true };
print("vector<bool> before erasing false", vb);
erase(vb, false);
print("vector<bool> after erasing false", vb);
}
C:\Temp>cl /EHsc /nologo /W4 /MTd meow.cpp
meow.cpp
C:\Temp>meow
string before: cute fluffy kittens
string after: ct flffy kttns
deque before: [10 11 12 14 15 17 18 19]
deque after: [10 12 14 18]
forward_list before: [10 11 12 14 15 17 18 19]
forward_list after: [10 12 14 18]
list before: [10 11 12 14 15 17 18 19]
list after: [10 12 14 18]
vector before: [10 11 12 14 15 17 18 19]
vector after: [10 12 14 18]
map before: [10/A 11/B 12/C 14/D 15/E 17/F 18/G 19/H]
map after: [10/A 12/C 14/D 18/G]
multimap before: [20/S 21/T 22/U 22/V 23/W 23/X 24/Y 25/Z]
multimap after: [20/S 22/U 22/V 24/Y]
set before: [10 11 12 14 15 17 18 19]
set after: [10 12 14 18]
multiset before: [20 21 22 22 23 23 24 25]
multiset after: [20 22 22 24]
unordered_map before: [18/G 10/A 19/H 11/B 12/C 14/D 15/E 17/F]
unordered_map after: [18/G 10/A 12/C 14/D]
unordered_multimap before: [20/S 21/T 22/U 22/V 23/W 23/X 24/Y 25/Z]
unordered_multimap after: [20/S 22/U 22/V 24/Y]
unordered_set before: [18 10 19 11 12 14 15 17]
unordered_set after: [18 10 12 14]
unordered_multiset before: [20 21 22 22 23 23 24 25]
unordered_multiset after: [20 22 22 24]
vector before erasing everything: [17 29 1729]
vector after erasing everything: []
vector before erasing nothing: [256 512 1024]
vector after erasing nothing: [256 512 1024]
empty vector before: []
empty vector after: []
list before erasing everything: [17 29 1729]
list after erasing everything: []
list before erasing nothing: [256 512 1024]
list after erasing nothing: [256 512 1024]
empty list before: []
empty list after: []
set before erasing everything: [17 29 1729]
set after erasing everything: []
set before erasing nothing: [256 512 1024]
set after erasing nothing: [256 512 1024]
empty set before: []
empty set after: []
vector<bool> before: [false true false false true true false true]
vector<bool> after: [false false false false]
vector before erasing 0: [0 11 0 0 22 33 0 0 44 0]
vector after erasing 0: [11 22 33 44]
list before erasing 0: [0 11 0 0 22 33 0 0 44 0]
list after erasing 0: [11 22 33 44]
set before erasing 0: [0 11 22 33 44]
set after erasing 0: [11 22 33 44]
vector<bool> before erasing false: [false true false false true true false true]
vector<bool> after erasing false: [true true true true]
(end)