Is the “right value reference” of C + + 11 template parameter a forward reference

Posted on

In C + + 11, & & is not only a logical and, but also a right-hand reference


void f(int&& i);

However, it may also be a forward reference:


template<typename T>
void g(T&& obj);

“Forwarding reference” is formerly known as “universal reference”. Its “universal” is that you can bind an lvalue to a forward reference, but not to an rvalue reference


void f(int&& i) { }

template<typename T>
void g(T&& obj) { }

int main()
{
  int n = 2;
  f(1);
// f(n); // error
  g(1);
  g(n);
}

In order to be a forwarding reference, the parameters of a function must satisfy the following requirements:

  • The parameter type is T & & without const or volatile;
  • T must be a template parameter for the function.

In other words, none of the parameters of the following functions are forward references:


template<typename T>
void f(const T&&);
template<typename T>
void g(typename std::remove_reference<T>&&);
template<typename T>
class A
{
  template<typename U>
  void h(T&&, const U&);
};

Another case is that the auto & & variable can also be a forward reference:


auto&& vec = foo();

So the best way to write a range for loop is to use auto & amp


std::vector<int> vec;
for (auto&& i : vec)
{
  // ...
}

There is an exception. When auto & & L = {1, 2, 3}; is on the right side of auto & & L, the variable is STD:: initializer_ List < int > & type.

Forward reference is used to forward. Write forward reference T & & only when your intention is to forward parameters. Otherwise, it is better to write const T & and T & & as overloads (if necessary, you can also write T & and const T & & which is not commonly used; where t is a specific type rather than a template parameter).

To forward a forward reference, you need to use STD:: forward, which is defined in < utility >:

There are several possible parameters to call G:

  • Int i = 1; G (I);, t is int &, call g (int &);
  • Const int j = 2; G (J);, t is const int & and calls g (const int &);
  • Int k = 3; G (STD:: move (k)); or G (4);, t is int (not int & & Oh!) , call g (int & &).

You may wonder why STD:: move doesn’t need < T > and STD:: forward does? Let’s start with the signature of STD:: forward


template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;

When STD:: forward is called, the compiler cannot remove according to STD:: forward_ reference_ T < T > reverses t to instantiate the function template, so < T > needs to be specified manually.

But it doesn’t answer the question fundamentally, or it can lead to a new question: why is the parameter of STD:: forward not defined as T &?

The reason is very simple. T & & will eat T -, const T -, T -, and const T & (as well as corresponding volatile). It is useless to write T & after T &.

Wait a minute. Do the T & amp; arguments match T & amp; in the incoming function?


#include <iostream>
#include <utility>

void foo(int&)
{
  std::cout << "int&" << std::endl;
}

void foo(const int&)
{
  std::cout << "const int&" << std::endl;
}

void foo(int&&)
{
  std::cout << "int&&" << std::endl;
}

void bar(int&& i)
{
  foo(i);
}

int main()
{
  int i;
  bar(std::move(i));
}

can’t! Program output int &. The value of I in the left bar is a reference type of the function. More directly, it has a name, so it’s an lvalue.

Therefore, if STD:: forward doesn’t have a template parameter that you specify manually, it won’t be able to distinguish between T & and T & — that would be “bad forwarding,” not “perfect forwarding.”.

Finally, the implementation of STD:: forward is analyzed. The following code is from libstdc + +:


template<typename _Tp>
 constexpr _Tp&&
 forward(typename std::remove_reference<_Tp>::type& __t) noexcept
 { return static_cast<_Tp&&>(__t); }

template<typename _Tp>
 constexpr _Tp&&
 forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
 {
  static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
         " substituting _Tp is an lvalue reference type");
  return static_cast<_Tp&&>(__t);
 }
  • Matches the first overload when the forwarding reference T & & obj binds the left value int +_ TP, that is, t is int & and returns the type_ TP & & is int & (reference folding: &, & &, & & are all folded to &, only & & & is folded to &);
  • Const int & the same principle;
  • When the forwarding reference binds the right value int & & to match the second overload_ TP is int, and the return type is int & &;
  • Const int & the same thing.

To sum up, STD:: forward can forward perfectly.

Programmers always have to hit the wall on the stack overflow to learn something.

This article about C + + 11 template parameters “right value reference” is a forward reference to this article introduced here, more relevant C + + 11 right value reference content please search the previous articles of developeppaer or continue to browse the related articles below, I hope you can support developeppaer more in the future!

Leave a Reply

Your email address will not be published.