5

When I have a code which 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 in main I call it:

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

The output is:
"void f(T&& arg): Can modify\n";
"void f(T&& arg): Can modify\n";

But when I change this code to non-generic, that is instead of function templates I'll 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 modify the parameter.";
"In f(MemoryBlock&&). This version can modify the parameter.";

It looks to me that only by changing functions from being templates to non templates changes totally the deduction rules for rvalue references.
Will be really greateful if somebody would explain that to me.

2
  • 1
    Your mislead by thinking that template parameter T maps to MemoryBlock. Commented Oct 16, 2015 at 14:18
  • It's interesting that you have "perfect forwarding" in the title but you don't do any forwarding at all. Commented Oct 16, 2015 at 14:30

2 Answers 2

5

When you use T&&, that's not an rvalue reference, that's a universal reference parameter. They're declared the same way, but they behave differently.

When you remove the template parameters, you're no longer in a deducible context and it's actually an rvalue reference: they bind only to rvalues, of course.

In a deducible context (that is when type deduction is taking place), T&& can be either an rvalue reference or an lvalue reference. Universal references can bind to virtually all combinations (const, const volatile, etc.) and in your case: const T&.

Now your train of thought was to be efficient and overload for rvalues and then lvalues, but what happens is that the universal reference overload is a better match when deducing template arguments. Therefore, it will be selected over the const T& overload.

Usually, you only want to keep the universal reference function and pair it up with std::forward<T>() to perfect forward the argument(s). This removes the need for your const T& overload, since the universal reference version will take over.

Please note that just because you see && in a deducible context, it does not mean that it is an universal reference; the && needs to be appended to the type being deduced, so here's an example of something that is actually an rvalue reference:

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 on the matter: https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

Sign up to request clarification or add additional context in comments.

1 Comment

@Artur Read also about reference collapsing rules.
1

T&& can be called as universal/forwarding reference.
reference collapsing rules:

  1. A& & becomes A&
  2. A& && becomes A&
  3. A&& & becomes A&
  4. A&& && becomes A&&

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

Here, the following apply:

  1. When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
  2. When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type 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 these both case

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

is the best choice for the compiler hence its taken instead of

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.