Perfect shipment and templates

When I have a code that looks like this:

template<class T> void f_(const T& arg) { cout << "void f(const T& arg): Cannot modify\n"; } template<class T> void f_(T&& arg) { cout << "void f(T&& arg): Can modify\n"; } 

and basically I call it:

 int main() { MemoryBlock block; f_(block); f_(MemoryBlock()); return 0; } 

Output:
"void f (T && arg): You can change \ n";
"void f (T && arg): May change \ n";

But when I change this code to non-generic, instead of function templates, I will have regular functions,

 void f(const MemoryBlock&) { cout << "In f(const MemoryBlock&). This version cannot modify the parameter.\n"; } void f(MemoryBlock&&) { cout << "In f(MemoryBlock&&). This version can modify the parameter.\n"; } 

the output is more "intuitive":
"In f (const MemoryBlock &). This version cannot change the parameter.";
"In f (MemoryBlock &). This version may change the parameter.";

It seems to me that only by changing functions from templates to non-templates completely change the output rules for rvalue links.
It will be really friendly if someone explains this to me.

+5
source share
2 answers

When you use T&& , this is not an rvalue reference, it is a universal reference parameter. They are declared the same, but they behave differently.

When you delete template parameters, you are no longer in the output context and actually refer to rvalue: they are bound only to rvalues, of course.

In the output context (that is, when type deduction occurs), T&& can be either an rvalue reference or an lvalue reference. Universal links can be associated with almost all combinations ( const , const volatile , etc.), and in your case: const T& .

Now your train of thought should be efficient and overload for rvalues ​​and then lvalues, but what happens is that universal reference overloading is the best match when outputting template arguments. Therefore, it will be selected on top of the overload const T& .

Usually you want to save a universal reference function and map it to std::forward<T>() in order to refine the argument (s). This eliminates the need for your const T& overload, since a universal reference version will be used.

Please note that just because you see && in the displayed context does not mean that it is a universal link; && should be added to the type being deduced, so here is an example of what the rvalue reference is actually:

 template<class T> void f_(std::vector<T>&& arg) // this is an rvalue reference; not universal { cout << "void f(T&& arg): Can modify\n"; } 

Here's a great talk about this: https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

+4
source

T&& can be called a universal/forwarding link.
link folding rules:

  • a & u becomes a &
  • and && & becomes A &
  • a && and becomes a &
  • and && & & becomes A &&

template<typename T> void foo(T&&);

Applicable here:

  • When foo is called on an lvalue of type A, then T allows A &, and therefore, according to the above rules for discarding arguments, the argument type effectively becomes A &.
  • When foo is called on an r-value of type A, then T resolves to A and therefore, the type of the argument becomes A & &.

In your case:

 template<class T> void f_(T&& arg); f_(block); //case 1 f_(MemoryBlock()); //case 2 

In case 1:
T = MemoryBlock & then T && becomes T && & ==> gives T &
In case 2:
T = MemoryBlock, then T && becomes T && ==> gives T &&

For both of these cases

 template<class T> void f_(T&& arg) 

is the best choice for the compiler, so instead of it

 template<class T> void f_(const T& arg) 
+1
source

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


All Articles