I turn into the eleventh standard from the old one and there are many new things for me, in particular, it is not clear why a double ampersand && is written in the parameters of functions. It is not clear what it is for and how it can be used?

Here is a small example:

void test(int && a){ a++; // как работать с таким параметром (а) внутри функции, например вывести ее на экран? } int main(){ int a = 1; test(a); // как передать переменную? return 0; } 
  • one
    This is the rvalue reference. A very big topic. - VladD
  • Here is an introductory article about them. - VladD
  • 3
    Thanks @VladD thought this is a basic knowledge. went to read - perfect
  • It says at the beginning of the article: “Rvalue links are a small technical extension of the C ++ language.” This is not quite true, the extension is far from small. - VladD
  • one
    @VladD, at 98 (remember exactly, because then I bought bc in the Lenknig) met with the source of five ( ***** ) pointers. The concept of "link" came later :) - PinkTux

2 answers 2

While reading, for yourself such a small rule has developed:

  1. type& - the argument is necessarily lvalue, i.e. what stands to the left of the equal sign:

     int a = 1; foo(a); // a может быть изменено в foo и по выходу иметь другое значение foo(1); // ошибка компиляции 
  2. const type& - the argument can be as lvalue (while guaranteeing that it will not change there) or rvalue, then its lifetime is extended for the duration of the call:

     int a = 1; bar(a); // а не меняется внутри bar(2); // тоже легально 
  3. type&& - the argument can only be rvalue, while inside the call you can change it and generally, you work as with a regular variable (as soon as the rvalue inside the function acquires a name, it becomes lvalue “automaton” :-)). To turn an ordinary variable into an rvalue, you need to "move" it, i.e. in essence, give ownership to the called function:

     int a = 1; baz(a); // ошибка компиляции baz(std::move(a)); // всё отлично, но после вызова состояние a не определено. baz(2); // тоже отлично, 2 - rvalue 
  4. const type&& - such a construction is possible, but its meaning for me skips. She's akin to this:

     void some(const int arg); 

    those. just forbid to change inside.

The rules imply usage: when you need to give ownership of the object. An additional overload is possible, so that you can work in the same style in the calling code with both rvalue and lvalue:

 void foo(int& a); // [1] void foo(int&& a); // [2] ... int a = 1; foo(a); // вызовется [1] foo(1); // вызовется [2] foo(move(a)); // вызовется [2], состояние 'a' будет неопределено. 

    To make it clearer what exactly && does, && 's compare three “classical” ways of passing an argument (this is precisely the use of “ampersands” that appear in the question):

    1. Transfer on the left-side link ( Type& ). This is the simplest case; only the pointer to the object is copied (and the link is at the lowest level).
    2. Pass by value . In this case, the compiler creates a full copy of the object by allocating memory on the stack and calling the copy constructor .
    3. And finally, the transfer on the right-hand link ( Type&& ). Here, the compiler also allocates space on the stack, however, it already causes the displacement constructor , whose task is to “move” all data from the original object to a new one, turning the first one into a “dummy” (safe from the point of view of lack of communication with the new object data).

      Why was “move” written in quotes? Yes, because in fact a simple copying is performed with the original zoning.

    The following question may arise: if it all comes down to simple copying with zeroing, why can't we do without the good old pass by value ? After all, we can assume that simply discarding the data of the original when it is destroyed is quite enough. The fact is that this is not always the case. A class can contain pointers (like, for example, std::vector ) or external resource descriptors (like std::fstream ). If you just copy the instances of these classes, we get:

    • or “deep” copying of the entire buffer for std::vector ,
    • or two references to the same file descriptor for std::fstream ; the destruction of one instance of a class will lead to the appearance of an incorrect descriptor in another.