Template parameter "rvalue reference" is a forward reference

In C ++ 11, there is &&no longer only the meaning of logical AND, it may also be an rvalue reference:

void f(int&& i);

But it is not necessarily true, &&it may be forwarding references:

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

"Forwarding reference" (forwarding reference) formerly known as "universal reference" (universal reference), its "universal" place is that you can bind an lvalue to the forwarding reference, but not to the 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);
}

For a function parameter to become a forward reference, it must satisfy:

  • The parameter type is T&&, no constOR volatile;

  • TMust be the template parameter of 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 situation is that auto&&variables can also become forward references:

auto&& vec = foo();

So forthe best way to write a range loop is to use auto&&:

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

There is one exception, when the auto&&right side is the initialization list, such as auto&& l = {1, 2, 3};, the variable is of std::initializer_list<int>&&type.

Forwarding references are used for forwarding. Only when your intentions are forwarding parameters before forwarding written references T&&, it's best to const T&and T&&written reload (if necessary can also be written T&, there is not commonly used const T&&; which Tis not the specific type of template parameters).

It is necessary to forward a forwarding reference std::forward, which is defined in <utility>:

#include <utility>

template<typename... Args>
void f(Args&&... args) { }

template<typename T>
void g(T&& obj)
{
    f(std::forward<T>(obj));
}

template<typename... Args>
void h(Args&&... args)
{
    f(std::forward<Args>(args)...);
}

gThere are several possible parameters for the call :

  • int i = 1; g(i);, TFor int&, call g(int&);

  • const int j = 2; g(j);, TFor const int&, call g(const int&);

  • int k = 3; g(std::move(k));Or g(4);, Tfor int(not int&&oh!), Call g(int&&).

You may wonder, why std::movedo <T>you std::forwardneed it instead of it? This has to start std::forwardwith the signature:

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

Call std::forward, the compiler can not be based on std::remove_reference_t<T>anti-launched T, so instantiation of a function template, so <T>you need to manually specify.

But this does not fundamentally answer the question, or may lead to a new question-why std::forwardare the parameters not defined T&&?

The reason is simple, T&&will T&, const T&, T&&and const T&&(and corresponding volatile) are eaten, with T&&the future, write T&useless.

And slow, will the T&&parameters match in the incoming function T&&?

#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));
}

will not! Program output int&. In the function bar, it iis an lvalue, whose type is intan rvalue reference. More directly, it has a name, so it is an lvalue.

Therefore, if std::forwardthere is no template parameter specified manually, it will not be able to distinguish between T&and T&&-it will be "bad forwarding" instead of "perfect forwarding".

Finally, analyze std::forwardthe implementation. 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);
  }
  • When the forwarding reference T&& objbound value left int&when the first overload match, _Tpi.e. Tas int&the return type _Tp&&for the int&(reference & &fold: & &&, , && &are folded &, only && &&folded &&);

  • const int&Empathy

  • When the forward reference is bound to the rvalue int&&, the second overload _Tpis matched, intand the return type is int&&;

  • const int&&Empathy.

In summary, it std::forwardcan be forwarded perfectly.

Programmers always have to hit the wall on Stack Overflow to learn something.

Guess you like

Origin www.cnblogs.com/jerry-fuyi/p/12733924.html