📜 ⬆️ ⬇️

Educational program for passing parameters by value to constructors and setters (modern C ++, examples)

Judging by the comments habr.com/en/post/460831/#comment_20416435 in the next post and the discussion that unfolded there, the article on Habré how to pass arguments to the constructor or setter will not hurt StackOverflow has a lot of similar material, but I don’t remember something here.

Because the example in that article is completely correct, and the author of the article is absolutely right. Here is an example:

// . struct person { person(std::string first_name, std::string last_name) : first_name{std::move(first_name)} //  , last_name{std::move(last_name)} // std::move  ! {} private: std::string first_name; std::string last_name; }; 

Such code allows you to cover all (well, almost all) possible options for using the class:

 std::string first{"abc"}, last{"def"}; person p1{first, last}; // (1)    person p2{std::move(first), last}; // !!!    person p2{std::move(first), std::move(last)}; // (3)   person p3{"x", "y"}; //   

Compare with the old method, when passed via const &: it is definitely worse, because it excludes option (3):
')
 // . struct person { person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: std::string first_name; std::string last_name; }; std::string first{"abc"}, last{"def"}; person p1{first, last}; //  ,    //      ,  first  last    // ?         //   0 !  const& . 

An alternative with && is also worse, but in the opposite direction:

 // . struct person { person(std::string&& first_name, std::string&& last_name) : first_name{std::move(first_name)} , last_name{std::move(last_name)} {} private: std::string first_name; std::string last_name; }; std::string first{"abc"}, last{"def"}; person p1{std::move(first), std::move(last)}; //  //       ,    &&  : person p2{std::string{first}, std::string{last}}; // FOOOO 

If you are not afraid of a combinatorial explosion, you can give a chance to && (but why? There will be no real gain in speed, the optimizer does not doze off):

 //   . struct person { person(std::string&& first_name, std::string&& last_name) : first_name{std::move(first_name)} , last_name{std::move(last_name)} {} person(const std::string& first_name, std::string&& last_name) : first_name{first_name} , last_name{std::move(last_name)} {} person(std::string&& first_name, const std::string& last_name) : first_name{std::move(first_name)} , last_name{last_name} {} person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: std::string first_name; std::string last_name; }; 

Or the same thing, only with templates (but then again, why?):

 //     (   ),      . struct person { template <typename T1, typename T2> person(T1&& first_name, T2&& last_name) : first_name{std::forward<T1>(first_name)} , last_name{std::forward<T2>(last_name)} {} private: std::string first_name; std::string last_name; }; 

Even if you do not have std :: string, but some object of your own written large class, and you want people to make it move (and not copy), then in this case it is better to prohibit the constructor from copying from this large class than to pass it everywhere by &&. This is more reliable, and the code is shorter.

Finally, a couple of options on how to do NOT WORTH:

 // . struct person { person(const std::string& first_name, const std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: //   :  ,     //     const std::string& first_name; const std::string& last_name; }; person p{"x", "y"}; // --,  

And so do not:

 // . struct person { person(std::string& first_name, std::string& last_name) : first_name{first_name} , last_name{last_name} {} private: //   ,      shared_ptr: //  ,   std::string& first_name; std::string& last_name; }; 

Why is this happening, what is the fundamental principle? It is simple: an object, as a rule, should OWN its properties.

If an object does not want to own something, then it can own shared_ptr for this “something”. By the way, in this case shared_ptr should also be passed by value, and not by constant link - there is no difference with the very first example at the beginning of the article:

 //  (  ). struct person { person(std::shared_ptr<portfolio> pf) : pf{std::move(pf)} // std::move     {} private: std::shared_ptr<portfolio> pf; }; auto psh = std::make_shared<portfolio>("xxx", "yyy", "zzz"); ... person p1{psh}; person p2{std::move(psh)}; // (X)  ,  psh    

Please note: std :: move for shared_ptr is completely legal, it eliminates the overhead of locking the shared_ptr link counter in memory (hundreds of CPU cycles) and its increment. It does not affect the lifetime of the object and other links to it. But (X) can be done, of course, only if the psh link in the code below is no longer needed.

Moral: do not use const & in general. Look according to circumstances.

PS
Use {} instead of () when passing constructor parameters. Fashionable, modern, youth.

PPS
In conclusion, one more thing: std :: move () actually does not move anything and in itself translates to zero assembler instructions. All that std :: move () does is to put a special “sticky label” on the link, turning it into an && - rvalue reference. And then you can separately “match” the type of the function parameter with this label (for example, have a separate function overload for the && - parameter and a separate one for the & -parameter). The meaning of the && - shortcut is to enable the calling code to tell the called: “if you want, you can eat the value from this link, I no longer need it; but only if you eat, leave the bones, then I still need to call the destructor for the remaining skeleton. " With the same success, it would be possible to transmit ordinary & -links (you can also “eat” an object with them), but with && it’s better semantics, because you will not confuse: where you can eat, and where you can only smell.

In this regard, the name std :: move () should be recognized as extremely unsuccessful. It would be correct to call it std :: eat_me_if_you_want () or std :: bon_appetit (). But std :: move () is shorter.

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


All Articles