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&&
, noconst
ORvolatile
; -
T
Must 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 for
the 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 T
is 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)...);
}
g
There are several possible parameters for the call :
-
int i = 1; g(i);
,T
Forint&
, callg(int&)
; -
const int j = 2; g(j);
,T
Forconst int&
, callg(const int&)
; -
int k = 3; g(std::move(k));
Org(4);
,T
forint
(notint&&
oh!), Callg(int&&)
.
You may wonder, why std::move
do <T>
you std::forward
need it instead of it? This has to start std::forward
with 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::forward
are 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 i
is an lvalue, whose type is int
an rvalue reference. More directly, it has a name, so it is an lvalue.
Therefore, if std::forward
there 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::forward
the 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&& obj
bound value leftint&
when the first overload match,_Tp
i.e.T
asint&
the return type_Tp&&
for theint&
(reference& &
fold:& &&
, ,&& &
are folded&
, only&& &&
folded&&
); -
const int&
Empathy -
When the forward reference is bound to the rvalue
int&&
, the second overload_Tp
is matched,int
and the return type isint&&
; -
const int&&
Empathy.
In summary, it std::forward
can be forwarded perfectly.
Programmers always have to hit the wall on Stack Overflow to learn something.