2

I was reading about rvalue references and perfect forwarding when I came across this article on MSDN: https://msdn.microsoft.com/en-us/library/dd293668.aspx

My question is about this example from the article:

#include <iostream>
#include <string>
using namespace std;

template<typename T> struct S;

// The following structures specialize S by 
// lvalue reference (T&), const lvalue reference (const T&), 
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of 
// the structure and its parameter.

template<typename T> struct S<T&> {
   static void print(T& t)
   {
      cout << "print<T&>: " << t << endl;
   }
};

template<typename T> struct S<const T&> {
   static void print(const T& t)
   {
      cout << "print<const T&>: " << t << endl;
   }
};

template<typename T> struct S<T&&> {
   static void print(T&& t)
   {
      cout << "print<T&&>: " << t << endl;
   }
};

template<typename T> struct S<const T&&> {
   static void print(const T&& t)
   {
      cout << "print<const T&&>: " << t << endl;
   }
};

// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t) 
{
   S<T&&>::print(std::forward<T>(t));
}

// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }

int main()
{
   // The following call resolves to:
   // print_type_and_value<string&>(string& && t)
   // Which collapses to:
   // print_type_and_value<string&>(string& t)
   string s1("first");
   print_type_and_value(s1); 

   // The following call resolves to:
   // print_type_and_value<const string&>(const string& && t)
   // Which collapses to:
   // print_type_and_value<const string&>(const string& t)
   const string s2("second");
   print_type_and_value(s2);

   // The following call resolves to:
   // print_type_and_value<string&&>(string&& t)
   print_type_and_value(string("third"));

   // The following call resolves to:
   // print_type_and_value<const string&&>(const string&& t)
   print_type_and_value(fourth());

}

My question is, why does this call:

print_type_and_value(s1);

resolve to:

print_type_and_value<string&>(string& &&t)

If my understanding is correct, string& && is an rvalue reference to an lvalue reference. Why is this? The variable s1 is an lvalue (it is not temporary, it is addressable, and it can be accessed from multiple parts of the program), so shouldn't the call resolve to string& (a simple lvalue reference)? I don't see where the double reference came from. s1 is a value, not a reference, isn't it? Why does this call involve rvalues at all?

In more general terms, I am a bit confused as to when template parameters resolve to T& && (an rvalue reference to an lvalue reference?) or T&& & (an lvalue reference to an rvalue reference?).

So, could someone please explain the following:

  1. Why did the call to print_type_and_value(s1) resolve to print_type_and_value(string& &&t) ?
  2. In general, when does f(var) resolve to f(T& &&x) or f(T&& &x) ?
  3. I've seen examples in which template parameters resolve to T&& &&, which looks to me like an rvalue reference to an rvalue reference. When does this happen?

Of course, I am aware of the reference collapsing rules, and I understand that T& & is collapsed to T&, but I'm wondering why the call in this example resolved to T& && in the first place.

Thanks in advance for your help!

Edit:

I understand the basics of reference collapsing, but one thing that I'd like to know is why this specific example behaved in the way it did.

Why did print_type_and_value(s1) resolve to print_type_and_value(string& &&t) and then collapse to print_type_and_value(string& t) ?

Edit 2:

Thanks a lot for your links! I'm starting to understand it.

I just have one more question. Why does the template type evaluate to string& when a variable of type string is passed?

Edit 3:

I've re-read the links you've posted, and I 100% get It now. Thanks again!

Ryan McCleary
  • 371
  • 2
  • 6
  • 1
    It's has to do with [reference collapsing](http://thbecker.net/articles/rvalue_references/section_08.html) – Alejandro Dec 30 '15 at 16:09

1 Answers1

1

The reference collapsing rule make print_type_and_value<string&>(string& &&t) equivalent to print_type_and_value<string&>(string& t): there are no reference to reference.

Here is an excellent question/answer on SO regarding this rule.

Community
  • 1
  • 1
YSC
  • 38,212
  • 9
  • 96
  • 149