Class shared_mutex
On some platforms shared_mutex is more effecient then shared_timed_mutex.
In C++17 was added shared_mutex.
Example of usage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
class MyCounter { public: int get() { std::shared_lock lock_(mutex_); return counter_; } void increment() { std::unique_lock lock_(mutex_); counter_++; } void reset() { std::unique_lock lock_(mutex_); counter_ = 0; } private: std::shared_mutex mutex_; int counter_{ 0 }; }; int main() { MyCounter counter; std::mutex cout_mutex; auto lambda = [&counter, &cout_mutex]() { std::lock_guard lock_(cout_mutex); for (auto i = 0; i < 3; i++) { counter.increment(); std::cout << counter.get() << std::endl; } }; std::thread thread1(lambda); std::thread thread2(lambda); thread1.join(); thread2.join(); return 0; } |
Interface sizes
In C++17 were added two variables:
Destructive interference size: a number that’s suitable as an offset between two objects to likely avoid false-sharing due to different runtime access patterns from different threads.
Constructive interference size: a number that’s suitable as a limit on two objects’ combined memory footprint size and base alignment to likely promote true-sharing between them.
Variables are used with alignas. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
struct keep_apart { alignas(std::hardware_destructive_interference_size) std::atomic<int> cat; alignas(std::hardware_destructive_interference_size) std::atomic<int> dog; }; struct together { atomic<int> dog; int puppy; }; struct kennel { // Other data members... alignas(sizeof(together)) together pack; // Other data members... }; static_assert(sizeof(together) <= hardware_constructive_interference_size); |
alias template void_t
void_t is used for simplify usage of SFINAE. void_t is declared like:
1 |
template <class...> using void_t = void; |
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class A { public: int qwe; }; template<typename T, typename = void> struct has_member_qwe : std::false_type {}; template<typename T> struct has_member_qwe<T, std::void_t<typename decltype(T::qwe)>> : std::true_type {}; int main() { std::cout << std::boolalpha; std::cout << has_member_qwe<A>::value << std::endl; return 0; } |
Alias template std::bool_constant
std::bool_constant it’s syntax sugar in C++17. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
void do_job(std::false_type) { /*Implementation for stuff (and char) */ } void do_job(std::true_type) { /*Implementation for integers but not char*/ } template <typename T> void DoJob(T) { do_job(std::bool_constant<std::is_integral<T>{} && !std::is_same<char, T>{}>()); } int main() { int a = 0; DoJob(a); return 0; } |
Logical operation metafunctions
In C++17 were added Variadic metafunctions conjunction, disjunction, and negation for metaprogramming. These traits short-circuit in the metaprogramming sense: template specializations that are not required to determine the result are not instantiated.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
template<typename T, typename... Ts> std::enable_if_t<std::conjunction_v<std::is_same<T, Ts>...>> func(T, Ts...) { std::cout << "all types in pack are T\n"; } template<typename T, typename... Ts> std::enable_if_t<!std::conjunction_v<std::is_same<T, Ts>...>> func(T, Ts...) { std::cout << "not all types in pack are T\n"; } int main() { func(1, 2, 3); func(1, 2, "hello!"); } |