 So hello everybody. This talk is going to be about C++17 and C++20 goodies. Now for, yes, it's even hydrogen. So for people who have been to academies before or who have met me before, I'm going to skip about me because there isn't really that much time. Usual disclaimer, make your code readable, pretend the next person who looks at your code is a psychopath and they know where you live. Very, very important note. I'm going to, I'm not going to explain what the code means. You should always sign the meaning by yourselves. Now the question is, why am I giving the same talk again? Because in Academy 2017, I already had a talk named C++17 and 20. That talk was a little bit focused on, let's say, bigger features of C++20 and some smaller features of C++17. Now the things have changed a little bit from 2017. We finally got C++20 ratified. So this is a photo from the proud meeting of ISOCTP where the C++20 was finalized. And two days ago, C++20 was accepted by the ISO. So it's now an official proper standard. And during those three years, all the compilers that we care about now support C++17. So I'm going to focus mostly on little useful features that C++17 gives us with a couple of C++20 features. I'm not going to cover any of the huge C++20 things. For those, you can go and watch the video from Academy 2017 or you can just find dedicated talks on ranges, coroutines and stuff. Okay, so let's begin. This is a little bit slow. So in C++11, we got a function called std to string. And this function is a little bit tedious because you can't convert strings to string. You can convert characters to string. Essentially, you can just convert things like integers and flows, et cetera. So if you wanted to write a function that extends the standard to string, we could do something like this. So we can create a generic function that obviously accepts any type T. And if we got a proper std string inside as a T, then we don't really have to call std to string. Now, in new beautiful C++17, we can check this at compile time where the type T is some specific type that we want. So we can just write if constexpr and then check is same T and std string. Now, in this particular slide, we have two features that didn't exist before C++17. The first one is that now we have prettier type traits. So all the type traits in the standard library, in the header type traits, have the suffix underscore b and underscore t. So instead of having to write is same something, something, column, column value, you can just say is same underscore b and it shortens the code a little bit. Much more important is that nowadays we don't need to write type name as the dk, column, column type or anything like that. You can just say stdk underscore t. So these type names, we had to sparkle over our code almost everywhere. Apart from having these suffixes in C++20 and expected in C++23, we have this quite, even in the cases where you still need to write type name in C++17 and 20. In C++23, it will all be just beautiful and you would always be able to assume that the compiler knows that you forgot to write the type name so you won't need to actually write it. Now for people that know what stdk is, I'd say that you can forget about it and that most of the time in C++20 you will be able to use stdremove cvrep. So if you have a type and that can be a const reference, volatile reference or anything else and you want to strip out all the reference parts and consts and volatile, you can just say stdremove cvrep underscore t and pass it to a certain type like const string, const string ref and this meta function will return you a normal string. So this is one of the things that is new, small thing that will make up your code much prettier in C++17. The second one is the compile time if or if constexpr. If we try to call this same v with a normal if and then try to put it in a function that checks whether something is a string, if it's a string then don't call it a string and if it's not a string then call it a string. Then you have a compiler error because both branches of if need to be valid. In this case, if you're using if constexpr, then only one of the branches will actually be compiled. Both of them need to be valid in syntax but the type t that we passed doesn't need to have a two string function to work on it if it's the same v returns true. So if you go to the first branch, the second branch will not really be checked for semantics. Also we can instead of just checking whether something is the same, we can also check std derived from. Now std derived from doesn't have underscore t because it's not a type trade, it's something that is available in C++17 and it's called the concept. In essence, for the time being just think of concepts as compile time functions that return true or false just like insane did. Just define with a different syntax. And if you want instead of supporting types that are not derived from a string, we can just call static assert in the else branch. Now this is going to lead to a problem because as I said both branches are going to be compiled but not checked semantically. In this case static assert is going to make a compiler error because else branch even if it's not going to be executed the else branch is going to be compiled. If you wanted to do something like a static assert of false what you should do instead is create a meta function like the function that is in the slide called always false. So whatever type you pass to it, it will return a false. And then you can use it in the static assert. So if the boolean value that you pass to static assert depends on t then it's not going to be evaluated in the else branch if only the then branch is going to be executed and compiled. Static assert without an explanation for the assert so without a second argument of the string is also added in C++17. So if you are lazy and you don't want to write an explanation for your assert since C++17 you can just omit it in the static assert. Now if we wanted to do it a little bit more modern we can just say okay we are creating a template function but we are going to restrict we don't want it to work for all t's we just want to want it to work on specific set of t's and we can just say it's a template function templated on t and we require that t is derived from sd string and this is the syntax that is available since concepts since C++20. If you want to provide several functions one function that works on types derived from string and another function that works on other types you can use this trick to restrict one of those functions by default if the restriction is fulfilled for a certain type that function will be called otherwise it will fall back to the second function the generic version without any requires clause. So this is concepts and constraints in C++20. Also if you don't like the requires clause you can always use the shortest syntax instead of saying template type name t and then requires you can just say template derived from string t and then implement your function and if this is also too much to type for you you can just say string to string not even it doesn't even look like a template function even if it is and then you can just say derived from string auto so automatically deduce the type that needs to be derived from sd string and you have the value of that type so this is the most third syntax for defining restrictions on generic or template functions. Another example of a couple of new things that we have in modern versions of C++ is this one where we have an if we are locking a mutex and we are checking whether a certain map contains a key KDE also we can do something like this we can write a range rate for loop again lock a mutex and then iterate through all the items in a map since we all know that maps are collections of pairs this is going to iterate over all the key values in a pair. So what new features do we have here? The first one is that we can add initializers to if, for and switch so the old four from Dark Ages from the C it already had a couple of clauses inside so the initializer, the condition and increment and nowadays in C++20 we have the same initializer for the range based for loop and since C++17 we have the initializers for if and for switches so if you want to narrow the scope of a variable you don't need to declare it in the outer scope and then just put it inside of a name block you can just put it inside of the if or for or switch. The second thing is that scope locks we didn't really have scope locks before we had something that was called lock guard and it was able to lock a single mutex that you pass through it. Scope lock is a little bit more powerful you can lock several mutexes at once and it uses nice algorithms to avoid deadlocks etc so if you need to lock several mutexes which arguably you shouldn't ever have to do you can use scope lock without any consideration of what locks first, what locks second etc. The second thing that is shown in this example is something that is actually not shown because it's missing lock guard and scope lock are generic template clauses so they need to have template arguments. In C++17 we had automatic type deduction for functions so if you call std make pair the compiler would automatically deduce the generic arguments so the template arguments for that function but it never worked for classes. In this case since C++17 the compiler can deduce that since MMAP mutex is an std mutex that this scope lock is actually a scope lock of std mutex for most of the generic classes that you see in the standard library and it can work obviously on your types that you define. So you don't need to write anymore std vector of int when you create a vector you can just say std vector axis of 103. Again the types are still not dynamic this is not Python. It's just that the compiler can deduce on the 103 that this is going to be a vector of int In the cases where you want to implement your own types the way that the compiler deduces the types for the class is either through a constructor so if you have a constructor that accepts a type t and your class is parameterized on the type t it will automatically deduce from the constructor if you call it with an int it's going t is going to be an int but in some cases you would like to help the compiler to do something a little bit more advanced. So if you don't have the constructors from which the compiler can deduce something you can write something that is called a deduction guide. In this case we have defined some type that is called my type and which we are trying to tell the compiler if somebody calls a constructor with an integer and a float then what it should deduce it should deduce my type of vector of integer and a float and you can write arbitrary deduction guidelines for your types sometimes the constructors are nicer sometimes the deduction guidelines will be prettier and they are usually more powerful. And another thing that was in the example that is from C++ 17 is the aforementioned structure bindings so we have on map, map is a collection of key value pairs so sd pair of something something and usually before C++ 17 we would iterate through a map and then access the keys and values as dot first and dot second. But first and dot second don't really convey any meaning as the names are really generic so if you want to assign the names to parts of a tuple or parts of a pair you can just use structure bindings with square braces and say key and value iterate take all the pairs from the map and assign dot first to key and dot second to value. Now there is something a little bit annoying about the structure bindings is that if we try to capture the key and value by value so not a construct or anything else we just wrote auto key and value from the map we would expect key and value to be proper values so we have an instance of let's say a string and instance of an integer internally this is a little bit more complex so the pair from the map will be stored inside of a proper value with the type std pair of something and key and value from the structure binding are going actually to be a reference to elements inside of the pair even if the compiler is going to try its hardest to lie to you and to tell you that it's a proper value even if you try to use deco type of key and deco type of value which will return that there are normal values that's not true they're actually internally they're going to be references to the elements inside of the std pair so they're not going to be proper values this has some unfortunate side effects that when you want to use more semantics on these things you can't really say return value and expect the compiler to optimize it for you you will have to return std move of value etc and the last section I have two minutes before the question stand this was really fast there's also some new things with standard algorithms so for example the algorithm std reduce which kind of behaves like accumulate but a little bit more generic now if you want to paralyze it you don't need to deal with threads to fire up threads anything else synchronization etc you can just say std reduce I wanted to be executed in parallel so std execution policy parallel and then just pass the same thing that you passed to normal std reduce now if you hate having to write .begin .end everywhere in your code you can use the second version if the slide ever switches you can sorry I have one thing before so again binary search the same thing as we used to have this binary search can be optimized really well if you have constexpr data so most of the algorithms in C++ have the constexpr added to them so if you have data that is known at compile time nothing will end up in executed dynamically the compiler will execute binary searches, sorts, accumulates and everything else during the compile time and to get back to .begin .end if you hate writing those then you should just use std ranges namespace and then instead of having to write pairs of iterators you can just write the collection itself so binary sphere search I'm trying to search 42 in the same way as I did in the last one and the last thing a little bit related to reduce and accumulate is something called fold expressions if you don't have a collection of values but you have a variadic template where you have a function that accepts numerous and arbitrary number of values you can use fold expressions to do the calculation or to reduce all the values so you can write 0 plus dot dot plus values and when you call a sum of 1 2 3 it will add 0 plus 1 plus plus 2 plus 3 fold expressions come in different forms depending on whether you want to go from the left to right with your operator or from the right to left this is used for quite complex things like if you want to invoke certain function on each of the values that you pass again this is not a collection just if you want to invoke it on all the arguments that the user passed to your function you can use the fold expressions operator and say std invoke f on values and it will just unwrap into std invoke f on the first value f on the second value f on the third value and so on another new thing in C++ 17 is the std invoke and this is a function that allows you to call not only lambda's ordinary functions on specific values but also to call pointers to member variables and pointers to member functions as if there were normal functions so you would be able to write something like for each call delete later from q object on window 1, windows 2 and window 3 and the last thing that I want to mention because my time has run out in C++ 20 we also got a new library for string formatting which is meant to obsolete the eos stream for the most part and we can write something like this to generate a really nice banner welcome to academy 22 and I think that's it if there are any questions thank you so much Iwan that was super interesting and a very beautiful banner we have three questions and I think we have time for the first one which is template functions or function template what's your stance so sorry function template it's usual that we call it template function but it's a function template it's a class template because they are not functions they are templates but in normal communication these two terms are quite interchangeable and then the last one do you have inside while didn't gain an initializer seems inconsistent so that was my first impression as well but while with the initializer is the old C4 just without the increment part so while with initializer would be the same as just writing for the first two clauses filled in and then semicolon with an empty third clause so it is inconsistent but it's not useful