23

The distinction between rvalue references and forwarding references was made clear enough in this example by Scott Meyers:

Widget&& var1 = someWidget;     // here, “&&” means rvalue reference (1)

auto&& var2 = var1;             // here, “&&” does not mean rvalue reference (2)

template<typename T>
void f(std::vector<T>&& param); // here, “&&” means rvalue reference (3)

template<typename T>
void f(T&& param);              // here, “&&”does not mean rvalue reference (4)

Essentially the distinction happens when we have a deducible context, hence case (3) explicitly states that we have a vector<...>&& whereas the T in case (4) is to be deduced and (after applying reference collapsing rules) categorized in terms of "value category".

But what happens with a bit more complex pattern matching? Take the following case for example :

template <template <class...> class Tuple, class... Ts>
void f(Tuple<Ts...>&& arg)
{

}

What does && mean here ?

vsoftco
  • 55,410
  • 12
  • 139
  • 252
Lorah Attkins
  • 5,331
  • 3
  • 29
  • 63

3 Answers3

17

In your last example, arg is an rvalue reference.

A forwarding reference is an rvalue reference to a cv-unqualified template parameter

and Tuple<Ts...> is not a template parameter.

(Citation from [temp.deduct.call].)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • What if I had `Tuple`. Does `Ts` behave like a forwarding references or are we just appending && to every parameter and typical reference collapsing applies? – Lorah Attkins Oct 09 '20 at 14:11
  • 1
    Lorah Attkins There’s no difference between what you’ve said. Appending && to a parameter and reference collapsing occurring is the mechanism by which forwarding references work. The key question is about how the types will be deduced and I don’t think the type deduction here will make it a forwarding reference. – JTanner Mar 04 '21 at 04:00
11

It is a rvalue reference, not a forwarding reference.

The easiest way to be sure is to try to pass an lvalue, if it fails, then it is a rvalue reference, if not, then a forwarding reference:

template<typename... Ts>
struct foo {};

//f function definition

int main() {
    foo<int, double> bar;
    f(bar); // fails! Cannot bind lvalue to rvalue reference
    f(foo<int, double>{}); // ok, rvalue is passed
}
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
3

The concept forwarding reference is not a standard concept, it is usefull de recognize it when you see it, but if you want to understand and deal with it correctly you must understand reference arithmetics. (I believe Meyer's book has also a chapter about it)

What is behind the concept of a forwarding reference is the reference arithmetic:

  • && && = &&
  • && & = &
  • & && = &
  • & & = &

Let's simulate compiler template type deduction with a forwarding reference

template<class T>
void foo(T&&);
//...
const int i=42;
foo(i); // the compiler will defines T = const int &
         //          T&&  = const int & && = const int &
         // => the compiler instantiates void foo<const int &>(const int &);
foo(6*7);// the compiler will defines T = int
         //          T&&  = int &&
         // the compiler instantiates  void foo<int>(int &&);

in such a situation, the instantiation of the template foo can produces a function wich takes argument by lvalue reference or functions which takes arguments rvalue reference: a forwarding reference is either a rvalue reference or a lvalue reference, depending on template type deduction. It is named like this, because in such a situation, the parameter shall be passed either as an lvalue or as an xvalue, and this is the job of T&& std::forward<T>(T&& a)

If you declare a function has:

 template<class T>
 void foo(ATemplateClass<T> && a);

whatever the type deduced for T by the compiler, you'll get a rvalue reference paramater.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Are you sure about your sample ? I believe that foo(42) and foo(6*7) are in the same boat here: https://godbolt.org/g/jOEK9b – RTempete Nov 28 '16 at 00:17
  • @RTempete That part of the post seems to be wrong: as you saw, both deduce to `int&&` - the same, and not `const`. If Oliv would explain why they think either should be `const`, and/or there should be some difference based on whether or not a (trivial, optimised away even at `-O0`) expression is involved, that would be a lot more useful. – underscore_d Nov 28 '16 at 19:00
  • Sorry, there were two errors in the exemple code. f(42)-> rvalue reference and f(6*7) also. – Oliv Nov 29 '16 at 09:28