Should I try to use r-value links as often as possible? Here, for example, the code:

std::string hi() { return "hello world\n" } auto&& str = hi(); 

In this case, in line 5, only one object is created, and the r-value reference to it, and no copying ... effectively, if we compare, for example, with:

 auto str = hi(); 

or

 auto str = std::move(hi()); 

Therefore, at first glance, the conclusion is: why not create r-value links as often as possible instead of the usual initialization of a variable? Especially if you need to create 100,000 times per second these variables. Suppose, if possible, write so:

 void function() { int&& a = 1; double&& b = 2.; auto&& value = return_value(); /*далее какой-то код*/ } 

continue to work with these links or transfer them to another place ... it looks like it is a bit more monstrous than the usual initialization of variables to which the eye is used:

 void function() { int a = 1; double b = 2.; auto value = return_value(); /*далее какой-то код*/ } 

Therefore, is it worth using r-value links as often as possible instead of the usual initialization of new variables in a local context (inside functions, for example) for efficiency, or are there any built-in optimizations that, for int a = 2 do the same actions as int&& a = 2 ?

  • Actually, something I can not figure out what kind of winnings are you hinting at? What do you think is the advantage int&&a = 2; before int a = 2; ? - Harry
  • do not judge strictly: I thought that int a = 2 is copied 2 into the variable a ... in the case of int && a = 2 there is no copying, there is only aliasing for the temporary object 2 ... maybe I'm wrong - xperious
  • 2
    @xperious: In general, constants do not create in advance temporary objects in memory to which a link could be attached. In order to make int&& a = 2 at run time, a temporary object of type int will be formed and initialized to value 2. And this is the same as int a = 2 . - AnT

3 answers 3

Links are usually stored in memory in the form of pointers to objects. Therefore, in this proposal

 auto &&a = 1; 

A temporary object will be created with a value of 1 and a link to it

If you have ad type

 auto a = f(); 

where the function f returns an object of some type T , then this object returned from the function is built on the place of the variable a , bypassing the call to the copying or moving constructor.

Consider the following example.

 #include <iostream> struct Int { Int( int x = 0 ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; } Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; } Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; } ~Int() { std::cout << "Int::~Int()" << std::endl; } int x; }; Int f( int x ) { return x; } int main() { auto x = f( 10 ); auto &&y = f( 20 ); return 0; } 

The output to the console will be as follows.

 Int::Int( int ) Int::Int( int ) Int::~Int() Int::~Int() 

That is, in both cases, one constructor will be invoked and, accordingly, the destructor.

In addition, you can not create, for example, arrays of the links. In the program below, if you uncomment a sentence with an array of links, you will receive an error message.

 #include <iostream> struct Int { Int( int x = 0 ) : x( x ) { std::cout << "Int::Int( int )" << std::endl; } Int( const Int &rhs ) : x( rhs.x ) { std::cout << "Int::Int( const Int & )" << std::endl; } Int( Int &&rhs ) : x( rhs.x ) { std::cout << "Int::Int( Int && )" << std::endl; } ~Int() { std::cout << "Int::~Int()" << std::endl; } int x; }; Int f( int x ) { return x; } int main() { Int a[] = { f( 10 ) }; //Int &&b[] = { f( 10 ) }; return 0; } 
  • If you add -fno-elide-constructors to your first example, the results will be slightly different. - αλεχολυτ
  • @alexolut Why add and complicate your life? :) - Vlad from Moscow
  • At least to show the difference, and the ability to optimize the non-reference compiler, even without any explicit options. - αλεχολυτ
  • but you can use a constant link to the array or r-value link to the array const Int (& ref) [5] = {1}; Int (&& ref _) [5] = {2}; - xperious

In fact, accessing a variable directly is always faster than by reference. Let's make a simple program:

 int O() { int x = 5; return x; } int test(){ auto&& value = O(); return value; } int test2(){ auto value = O(); return value; } 

We collect WITHOUT optimizations.

 test(): push rbp mov rbp, rsp sub rsp, 16 call O() mov DWORD PTR [rbp-12], eax lea rax, [rbp-12] mov QWORD PTR [rbp-8], rax mov rax, QWORD PTR [rbp-8] mov eax, DWORD PTR [rax] leave ret test2(): push rbp mov rbp, rsp sub rsp, 16 call O() mov DWORD PTR [rbp-4], eax mov eax, DWORD PTR [rbp-4] leave ret 

As you can see the work directly - fewer operations at once, although we did not use the variable count. In general, for primitives, the variable is definitely better; for complex classes, use a profiler (it may be worthwhile to use a displacing constructor or something).

  • hmm, yes, my idea about the ubiquitous use of r-value reference was wrong ... if it’s without optimizations - xperious
  • Well, uhh, the fact that without optimizations the compiler collects stupid code is not an argument. If the semantics of the code are the same, then in the ideal case the compiler should produce the same code. - VladD
  • @VladD is certainly the same on -O2 code, but there is no function call there. What if the code is more complicated? In any case, in terms of optimization, the link is no better than a variable. - pavel
  • @pavel: Well, yes, but you can go so far that you can replace the for (auto x : container) type code with a loop with a counter, for reasons that the optimizer cannot cope. It seems to me that the main considerations should be the considerations of clarity, readability and ease of support. - VladD
  • @pavel refuse links and begin to transfer large objects by value? - αλεχολυτ

In this case, there is no practical difference between

 auto str = hi(); 

and

 auto&& str = hi(); 

In both cases, copy elision and return value optimization result in the result of the hi function being formed (constructed) directly in the final object. In the first case, directly in str . In the second case, directly in the temporary object to which the str will bind.

  • after all, it is not clear ... here is an object that weighs 2mb, let's say ... it has a complicated copy and move constructor (even if there is a deep copy) ... really, with auto ref = return_value (); 100% rvo will work and it is not profitable to write auto && ref = return_value ()? - xperious
  • @xperious: The complexity of the constructor and the severity of the object does not affect anything here. Only the desire of the compiler to work according to the scheme of such a direct construction affects. In C ++ 17, by the way, many of these optimizations (if not all) become guaranteed. But in the current C ++ they work fine, which you can easily see in practice. - AnT