seed_seqThe utility std::seed_seq consumes a sequence of integer-valued data and produces a requested number of unsigned integer values. It provides a way to seed multiple random engines or generators that require a lot of entropy.
For example, the internal state of the mt19937 generator is represented by 624 integers, hence the best way to seed it is to fill it with 624 numbers based on a high-entropy source (e.g., the random_device provided by the operating system):
std::random_device rd{};
std::array<std::uint32_t, 624> seed_data;
std::generate(seed_data.begin(), seed_data.end(), std::ref(rd));
std::seed_seq seq(seed_data.begin(), seed_data.end());
std::mt19937 gen{seq};
You can use the generated seeds to feed different random engines.
In C++, you can shuffle a range of elements using the std::shuffle utility from the <algorithm> header. It shuffles the elements randomly so that each possible permutation has the same probability of appearance. Here's an example:
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::random_device rd{};
std::default_random_engine g{rd()};
std::shuffle(v.begin(), v.end(), g);
Every time you run this code, the vector v will be shuffled differently.
Another useful utility in <algorithm> is std::sample, which extracts n elements from a range without repetition and inserts them into another range. Here's an example:
int n = 10;
std::vector<double> p;
// Fill p with more than n values to sample.
std::vector<double> res;
auto seed = std::random_device{}();
std::sample(p.begin(), p.end(), std::back_inserter(res), n, std::mt19937{seed});
This code generates a different realization of the sample every time you run it.
C++ provides three common clocks:
std::chrono::system_clock: Represents the system-wide real-time clock. It's suitable for measuring absolute time (can change if the user changes the time on the host machine).std::chrono::steady_clock: Represents a steady clock that never goes backward. It's suitable for measuring time intervals and performance measurements.std::chrono::high_resolution_clock: May provide the highest resolution available, but is often just an alias for steady_clock. Prefer steady_clock for portability.void my_function() {
// Code to measure.
}
auto start = std::chrono::high_resolution_clock::now();
my_function();
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "Time taken by function: "
<< duration.count() << " microseconds" << std::endl;
void my_function() {
// Code to measure.
}
const int num_iterations = 1000;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < num_iterations; ++i) {
my_function();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << "Average time taken by function: "
<< duration.count() / num_iterations << " microseconds" << std::endl;
std::chrono duration literals (since C++14)#include <chrono>
using namespace std::chrono_literals;
// Old way (C++11):
auto duration1 = std::chrono::milliseconds(100);
auto duration2 = std::chrono::seconds(2);
auto duration3 = std::chrono::minutes(5);
// New way (C++14):
auto duration1 = 100ms;
auto duration2 = 2s;
auto duration3 = 5min;
auto total = 1h + 30min + 45s;
// Timing with literals:
auto start = std::chrono::high_resolution_clock::now();
my_function();
auto end = std::chrono::high_resolution_clock::now();
auto duration = end - start;
if (duration > 100ms) {
std::cout << "Function took more than 100ms!" << std::endl;
}
A full set of utilities to manipulate files, directories, etc. in a filesystem became available.
const auto path{"big/file/to/copy"};
try {
if (std::filesystem::exists(path)) {
const auto file_size{std::filesystem::file_size(path)};
std::filesystem::path tmp_path{"/tmp"};
if (std::filesystem::space(tmp_path).available > file_size) {
std::filesystem::path new_dir = tmp_path / "example";
std::filesystem::create_directory(new_dir);
std::filesystem::copy_file(path, new_dir / "new_file");
}
}
} catch (const std::filesystem::filesystem_error& e) {
std::cerr << "Filesystem error: " << e.what() << std::endl;
}
Smart pointers:
std::unique_ptr by default for single ownershipstd::shared_ptr only when shared ownership is truly neededstd::make_unique/std::make_shared for constructionnew/delete in modern C++std::weak_ptr to break cycles in shared pointer graphsMove semantics:
std::move to explicitly transfer ownershipSTL utilities:
std::chrono::steady_clock for timing measurements<filesystem> for portable file operationsC++ is continuously evolving, and to maintain backward compatibility, new features are added while very few, if any, are eliminated. However, if you adopt a specific programming style, you'll find yourself using only a subset of what C++ has to offer.
The more outdated and cumbersome features that make programming more complex and less elegant will gradually be used less and less.
It's advisable to start incorporating the new features that genuinely assist you in writing cleaner, simpler code. Most of the features illustrated here move in that direction.
But always remember: the most important aspect of your code is whether it accomplishes the right task. An elegant code that yields incorrect results is of no use.