ch25.other_utility_n_algos
C++17 - The complete guide, Ch 25: Other Utility Functions and Algorithms
25.1 size(), empty(), and data()
- To support the flexibility of generic code, the C++ standard library provides three new helper functions:
size(),empty(), anddata(). - Just like the other global helpers for generic code iterating over ranges and collections,
std::begin(),std::end(), andstd::advance(), these functions are defined in the header file<iterator>.
25.1.1 Generic size() Function
- The generic
std::size()function allows us to ask for the size of any range as long as it has an iterator interface or is a raw array. - Note that this function template therefore replaces the usual way computing the size of an array using countof or ARRAYSIZE defined as something like:
#define ARRAYSIZE(a) (sizeof(a)/sizeof(*(a)))
25.1.2 Generic empty() Function
- Similar to the new global size(), the new generic std::empty() allows us to check whether a container, a raw C array, or a std::initializer_list<> is empty.
- Thus, similar to the example above, you can generically check whether a passed collection is empty:
if (std::empty(coll)) {
return;
}
- In contrast to
std::size(),std::empty()also works for forward lists. - Note that, according to language rules, raw C arrays cannot have a size of zero. Therefore,
std::empty()for raw C arrays is specified to always return false.
25.1.3 Generic data() Function
- The new generic
std::data()function allows us to give access to the raw data of collections (containers that have adata()member, raw C arrays, orstd::initializer_list<>s).
template <typename T>
void printData(const T& coll) {
// print every second element:
for (std::size_t idx{0}; idx < std::size(coll); ++idx) {
if (idx % 2 == 0) {
std::cout << std::data(coll)[idx] << ' ';
}
}
std::cout << '\n';
25.2 as_const()
- The new helper function
std::as_const()converts values into the corresponding const values without usingstatic_cast<>or theadd_const_t<>type trait. - It allows us to force calling the const overload of a function for a non-const object in case this makes a difference:
std::vector<std::string> coll;
foo(coll); // prefers a non-const overload
foo(std::as_const(coll)); // forces using a const overload
- If
foo()is a function template, this would also force the template to be instantiated for a const type rather than the original non-const type. - One application of
as_const()is the ability to capture lambda parameters by const reference
25.2.1 Capturing by Const Reference
std::vector<int> coll{8, 15, 7, 42};
auto printColl = [&coll = std::as_const(coll)] {
std::cout << "coll: ";
for (int elem : coll) {
std::cout << elem << ' ';
}
std::cout << '\n';
}
## 25.3 clamp()
- “clamp” a value between a passed minimum and maximum value. It is a combined call of min() and max()
int main() {
for (int i : {-7, 0, 8, 15}) {
std::cout << std::clamp(i, 5, 13) << '\n';
}
// v -----> clamp gives the middle value
// 5 {-7 5 13}
// 5 {0 5 13}
// 8 {5 8 13}
// 13 {8 13 15}
// can also pass lambda
for (int i : {-7, 0, 8, 15}) {
std::cout << std::clamp(i, 5, 13, [](auto a, auto b) {
return std::abs(a) < std::abs(b);
}) << '\n';
}
// input -7 return -7, as abs(-7) is the middle of {5, 7, 13}
}
25.4 sample()
- an algorithm that extracts a random subset (sample) from a given range of values (the population). This is sometimes called reservoir sampling or selection sampling
// print 10 randomly selected values of this collection:
std::sample(coll.begin(), coll.end(),
std::ostream_iterator<std::string>{std::cout, "\n"}, 10,
std::default_random_engine{});
We pass:
- Begin and end of the range we extract the subset of values from
- An iterator used to write the extracted values to (here an ostream iterator writing them to standard output)
- The maximum number of values to extract (we may extract fewer values if the range is too small)
- The random engine used to compute the random subset
// initialize a Mersenne Twister engine with a random seed:
std::random_device rd; // random seed (if supported)
std::mt19937 eng{rd()}; // Mersenne twister engine
// initialize destination range (must be big enough for 10 elements):
std::vector<std::string> subset;
subset.resize(100);
// copy 10 randomly selected values from the source range to the destination
// range:
auto end = std::sample(coll.begin(), coll.end(), subset.begin(), 10, eng);
// print extracted elements (using return value as new end):
std::for_each(subset.begin(), end,
[](const auto& s) { std::cout << "random elem: " << s << '\n'; });