Refresh my C++ 11 brain and upgrade to 17...
Why?
After learning Rust, I feel ready to absorb some good parts of modern C++.
Book: Modern C++(⭐⭐⭐)
I found the book can be compressed to a few bullet points for myself:
Cheatsheet
- use nullptr instead of NULL
- constexpr for e.g. function fibonacci of compile time; also prefer use constexpr instead of const for compile time constants
- if/switch ( tmp variable ; ...) allow variables like for loop
- std::intializer_list<int> -> { 1, 2, 3} for sizable input
- tuple and destructor auto [x, y, z] = std::make_tuple<int, double, std::string>(...); std::get<0>(tuple) get 0 element
- destructor assignment works for many other types, pair, POD struct, array, etc.
- get auto type: std::is_same<decltype(x), int>::value
- if constexpr( compile time if branch) {...
- use 'using' instead of typedef
- auto can be return value for template and can be parameters for functionals
- default template type template<typename T = int, typename U = int >
- skipped some parts due to sick of any unreadable meta programming ... unless you want to implement refection
- use enum class:type instead of enum
- constructor can call other constructor. Yay!
- inherit constructor with 'using Base::Base';
- java flavor override and final: virtual foo() override; or virtual foo() final;
- explicitly constructors: Magic()= default; Magic(const Magic& )=delete; Magic& operator=(const Magic &)=delete;
- lambda: [&](args) -> returnee {...} [] can be empty or values that want to capture in function when contructing lambda
- use std::function<...> instead of pointer
- bind function: std::bind(foo, std::placeholders::_1, 1,2) new function takes only one arg
- T&& extends the life of rvalue and can change it. while T&& itself as ref is lvalue.
- vec.push_back(std::move(str)) here move change the temp rvalue to left value and saved copy contructing
- prefer std::array to T[] and you can get pointer by std::array.data()
- std::forward_list single linked list vs std::list double linked list
- std::shared_ptr get() reset() use_count() std::make_shared<T>(0) to create
- std::regex_match(std::string, std::regex)
- std::thread(lambda, args) and then join()
- std::future<T> f = std::async(std::launch::async, []{ return t; }); and f.wait();
- new lock guard std::unique_ptr > std::lock_guard<std::mutex > lock(a_mutex);
- std::atomic<T> fetch_add() fetch_sub() and is_lock_free to check if support on platform
- std::this_thread::sleep_for(std::chrono::milliseconds(900));
- Raw multiline string R"()";
- alignof and alignas. Wow, my ancient memory of struct alignment.
My Addition
The book didn't cover every interesting aspect and filesystem is missing (TODO). So here I did some adding for complete:
- use std::string_view replacing c style string or every c++ const std::string&, and get bonus like start_with(), substr() and better performance etc. view is read only but don't use in case when immutable is required like e.g. key for hashmap.
- initialize class member inside class like integer and for
static
you just need to addinline
- std::optional<T> std::nullopt and error handling
- std::iota(v.begin(), v.end(), start_value) == range_set_container(start_value)
- std::ref and std::cref return std::reference_wrapper for convenience (e.g. shuffle list using vector referencing to the list element)
- std::swap and std::iter_swap
- other good stuff in algorithms e.g. copy, shuffle, sort etc.
- std::remove_const to remove const and const_cast
- get type name typeid(variable).name()
- static_assert(sizeof(CLASS)==16) for compile time
My Practice Notes
// Types
using i32=int;
using u32=unsigned int;
using u64=unsigned long long int;
using i64=long long int;
using i8=char;
using u8=unsigned char;
// replace every old array with this...
constexpr std::array arr = {"hello", "world"};
cout << arr.size(); //no more sizeof(T[])/sizeof(T) and basically vector's a few read method
//same value for every element?
arr.fill(5); // or std::to_array(), ancient c style can intial to 0 when ={0} though
// always prefer string_view to char[]
string_view v = str /*const char* or string*/;
while(i< v.size() && isspace(v[i])) ++i; while(j>i && isspace(v[j])) --j;
cout << v.substr(i, j-i+1);
string s{v.data(), v.size()}; //copy back to a new string
// Lambda instead of functor
sort(v.begin(), v.end(), [/*capture list here*/](const auto& x, const auto& y) -> bool {return x < y; });
// to implement similar algorithm
template<typename InputIterator, typename InputIterator, typename Compare>
sort(InputIterator begin, InputIterator end, Compare cmp);
for_each(map.begin(), map.end(), [&v] (const std::pair<K,V> &entry) { v.push_back(entry); });
transform(map.begin(), map.end(), destination, lambda)
// GetOrDefault
(map.emplace(key, value)).first->first or second; // .first is iterator of entry .second is bool
// Heavy String manipulate and different type e.g. string_view
stringstream << char * << string_view& << string&
stringstream >> int; and stoi
// Random #
default_random_engine eng;
uniform_int_distribution<int> dist(0,100);
dist(eng) -> random number in [0, 100]
// String and values
to_chars(first, last, any primitive type)
from_chars(first, last, out, base)->from_chars_result = { *ptr, errc::out_of_range } // check if e == errc()
to_string & stoi // throws and locale stof stod
// startwith
str.compare(0, prefix.size(), prefix) == 0
// Tokenize: getline(sstr, output, '.') is old fashion and char delimiter
sregex_token_iterator it(str.begin(), str.end(), regex(R"[.]+"), -1); // powerful
// Compare
auto myCmp = [](auto& x, auto& y)->bool{return x < y;} // less as default
sort(first, last, myCmp);
priority_queue<T, vector<T>, decltype(myCmp)> pq(myCmp); // high priority first(max value) with default less comparator
// compare vector etc.
vector<int> A, B;
A < B; //as in lexico order
tuple<int, int, int> C, B;// similar to vector
// String manipulate is painful in c++
// C++20 string
string x = std::format("{:.2f}", a); //float a; goodbye to stringstream
// Override hash for your object
auto my_hash = [](const int& x)->size_t{
hash<int>(x);
};
unordered_map<int, int, decltype(my_hash)> hashmap
// gcd lcm
gcd(x, y)
lcm(x, y)
// 2d array // change int DIR[4][2] = { {1, 0}, {-1,0}, {0, 1}, {0,-1} }; // to (note extra brackets) array<array<int, 2>, 4> = {{ {1, 0}, {-1,0}, {0, 1}, {0,-1} }};