📜 ⬆️ ⬇️

Modern C ++ features that all programmers need to be aware of

The author of the material, the translation of which we are publishing today, says that C ++, in its current form, when compared with what this language was a few years ago, has changed significantly for the better. Of course, these changes did not happen immediately. For example, in the old days, C ++ lacked dynamism. It was not easy to find a person who could say that he has tender feelings for this language. Everything changed when those responsible for the standardization of the language decided to launch the innovations. In 2011, C ++ became a dynamic language, a language that is constantly evolving and causes programmers to have much more positive emotions.

Do not think that the language has become easier. It can still be called one of the most complex widely used programming languages, if not the most difficult. But modern C ++ has become much friendlier than before.


')
Today we will talk about some new features of the language (starting with C ++ 11, which, by the way, is already 8 years old), which any programmer will know about.

Auto keyword


Since the auto keyword appeared in C ++ 11, the life of programmers has become easier. Thanks to this keyword, the compiler can output the types of variables at compile time, which saves us from having to always specify the types ourselves. This turned out to be very convenient, for example, in cases when you have to work with data types like map<string,vector<pair<int,int>>> . When using the auto keyword you need to take into account some features. Consider an example:

 auto an_int = 26; //    ,     - int auto a_bool = false; //   bool auto a_float = 26.04; //   float auto ptr = &a_float; //       auto data; // #1     ?    - . 

Pay attention to the line, the last one in this example, the comment to which is marked as #1 (hereinafter, in a similar way, we will mark the lines that we will, after the examples, parse). There is no initializer in this line, you cannot do this. The code located in this line does not allow the compiler to know what the type of the corresponding variable should be.

Initially, the capabilities of the auto keyword in C ++ were pretty limited. Then, in more recent versions of the language, auto features were added. Here is another example:

 auto merge(auto a, auto b) //            auto! {   std::vector<int> c = do_something(a, b);   return c; } std::vector<int> a = { ... }; // #1 -  std::vector<int> b = { ... }; // #2  -  auto c = merge(a,b); //       

In lines #1 and #2 , the variable is initialized using curly brackets - another new feature of C ++ 11.

Remember that when using the auto keyword, the compiler must have some way to infer the type of the variable.

Now - an interesting question. What happens if you use a construct like auto a = {1, 2, 3} ? What is it? Vector, or the reason for the compilation error?

Actually, in C ++ 11 a structure of the form std::initializer_list<type> . The list of initialization values ​​in parentheses will be treated as a container when using the auto keyword.

And finally, as has already been said, type inference by the compiler can be extremely useful if you have to work with complex data structures. Here is an example:

 void populate(auto &data) { //    !   data.insert({"a",{1,4}});   data.insert({"b",{3,1}});   data.insert({"c",{2,3}}); } auto merge(auto data, auto upcoming_data) { //         auto result = data;   for(auto it: upcoming_data) {       result.insert(it);   }   return result; } int main() {   std::map<std::string, std::pair<int,int>> data;   populate(data);   std::map<std::string, std::pair<int,int>> upcoming_data;   upcoming_data.insert({"d",{5,3}});   auto final_data = merge(data,upcoming_data);   for(auto itr: final_data) {       auto [v1, v2] = itr.second; // #1               std::cout << itr.first << " " << v1 << " " << v2 << std:endl;   }   return 0; } 

Take a look at line #1 . The expression auto [v1,v2] = itr.second is a new C ++ 17 feature. This is the so-called decomposition when declaring variables. In previous versions of the language, it was necessary to extract each value separately. Thanks to this mechanism, it has become much more convenient to perform such operations.

Moreover, if you need to work with data using links, it is enough to add just one character to this construct, converting it to the following form: auto &[v1,v2] = itr.second .

Lambda expressions


C ++ 11 introduces support for lambda expressions. They resemble anonymous functions in JavaScript, they can be compared with functional objects without names. They capture variables in different scopes depending on their description, for which compact syntactic constructs are used. In addition, they can be assigned to variables.

Lambda expressions are a very useful tool for those cases when you need to perform some small operation in the code, but you do not want to write a separate function for this. Another common example of their use is the creation of functions used when comparing values. For example:

 std::vector<std::pair<int,int>> data = {{1,3}, {7,6}, {12, 4}}; //        std::sort(begin(data), end(data), [](auto a, auto b) { //   - auto!   return a.second < b.second; }); 

In this brief example you can find a lot of interesting things.

First of all, pay attention to how convenient it is to use variable initialization using curly brackets. Next, we can see the standard begin() and end() constructs, which also appeared in C ++ 11. Then comes the lambda function used as a mechanism for comparing data. The parameters of this function are declared using the auto keyword, this feature appeared in C ++ 14. Previously, this keyword could not be used to describe the parameters of functions.

Now notice that the lambda expression starts with square brackets - [] . This is the so-called mask of variables. It defines the scope of the expression, that is, it allows you to manage the relationship of the lambda expression with local variables and objects.

Here is an excerpt from this repository on modern C ++ features:


As a result, if you need to convert data into some other format inside the lambda function, you can use the mechanisms described above. Consider an example:

 std::vector<int> data = {2, 4, 4, 1, 1, 3, 9}; int factor = 7; for_each(begin(data), end(data), [&factor](int &val) { //    factor     val = val * factor;   factor--; // #1   - ,  -    factor   }); for(int val: data) {   std::cout << val << ' '; // 14 24 20 4 3 6 9 } 

Here, if the factor variable would be accessed by value (then, when describing the lambda expression, the mask of the [factor] variables would be used), then in line #1 the factor value could not be changed - simply because we would not have rights to performing such an operation. In this example, we have the right to such actions. In such situations, it is important not to abuse the possibilities that access to variables has by reference.

In addition, note that access to val also carried out by reference. This allows you to ensure that data changes occurring in the lambda function affect the vector .

Variable initialization expressions inside if and switch constructs


I really liked this new C ++ 17 right after I found out about it. Consider an example:

 std::set<int> input = {1, 5, 3, 6}; if(auto it = input.find(7); it==input.end()){ //   - ,  -    std::cout << 7 << " not found" << std:endl; } else {   //    else      it   std::cout << 7 << " is there!" << std::endl; } 

It turns out that now you can perform initialization of variables and comparisons with their use in a single if or switch block. This contributes to the writing of neat code. Here is a schematic description of the considered construction:

 if( init-statement(x); condition(x)) {   //    } else {   //     x   //    } 

Perform calculations at compile time using constexpr


The constexpr gives us great opportunities. Suppose we have a certain expression that needs to be calculated, while its value, after it initializes the corresponding variable, will not change. Such an expression can be calculated in advance and used as a macro. Or, what has become possible in C ++ 11, use the constexpr .

Programmers strive to minimize the amount of computation performed during the execution of programs. As a result, if certain operations can be performed during the compilation process and thus relieve the load on the system during program execution, this will have a good effect on the program behavior at run time. Here is an example:

 #include <iostream> constexpr long long fact(long long n) { //       constexpr return n == 1 ? 1 : (fact(n-1) * n); } int main() { const long long bigval = fact(20); std::cout<<bigval<<std::endl; } 

This is a very common use of constexpr .

Since we declared a function for calculating factorial as constexpr , the compiler can calculate the fact(20) value in advance during the compilation of the program. As a result, after compilation, the string const long long bigval = fact(20); can be replaced by const long long bigval = 2432902008176640000; .

Note that the argument passed to the function is represented by a constant. This is an important feature of using functions declared using the constexpr . The arguments passed to them must also be declared using the constexpr or using the const keyword. Otherwise, such functions will behave like normal functions, that is, during compilation, their values ​​will not be calculated in advance.

Variables can also be declared using the constexpr . In such a case, as it is easy to guess, the values ​​of these variables must be calculated at compile time. If this cannot be done, a compilation error message will be displayed.

It is interesting to note that later, in C ++ 17, the constructs constexpr-if and constexpr-lambda appeared .

Tuple data structures


Like the pair data structure, the tuple data structure (tuple) is a collection of values ​​of different types of fixed size. Here is an example:

 auto user_info = std::make_tuple("M", "Chowdhury", 25); //  auto     //    std::get<0>(user_info); std::get<1>(user_info); std::get<2>(user_info); //  C++ 11     tie std::string first_name, last_name, age; std::tie(first_name, last_name, age) = user_info; //  C++ 17, ,       auto [first_name, last_name, age] = user_info; 

Sometimes, instead of a tuple data structure, it is more convenient to use std::array . This data structure is similar to simple arrays used in the C language, with additional features from the standard C ++ library. This data structure appeared in C ++ 11.

Automatic output of class template argument type


The name of this feature looks rather long and complicated, but in reality there is nothing difficult here. The basic idea here is that in C ++ 17, the output of template argument types is also performed for standard class templates. Previously, this was supported only for functional templates. As a result, it turns out that before they wrote:

 std::pair<std::string, int> user = {"M", 25}; 

With the release of C ++ 17, this construction can be replaced with this one:

 std::pair user = {"M", 25}; 

Type inference is implicit. This mechanism is even more convenient to use when it comes to tuples. Namely, before you had to write the following:

 std::tuple<std::string, std::string, int> user ("M", "Chy", 25); 

Now the same thing looks like this:

 std::tuple user2("M", "Chy", 25); 

It is worth noting here that these features will not seem to be something worthy of attention for those who are not particularly familiar with C ++ templates.

Smart pointers


Working with pointers in C ++ can be a real nightmare. Due to the freedom that the language gives the programmer, it is sometimes very difficult for him, as they say, “do not shoot yourself in the foot.” In many cases, it is pointers that push the programmer to such a “shot”.

Fortunately, in C ++ 11, smart pointers have appeared that are much more convenient than regular ones. They help the programmer avoid memory leaks, freeing resources whenever possible. In addition, they provide a guarantee of security for exceptions.

Results


Here is a good repository, in which, we believe, it will be interesting to look into those who follow C ++ innovations. Something new is constantly appearing in this language. Here we touched on only a few modern features of the language. In fact, a lot of them. It is possible that we will talk more about them.

Dear readers! What modern features of C ++ seem to you the most interesting and useful?

Source: https://habr.com/ru/post/451870/


All Articles