问题
I suppose when a universal reference parameter is matched with an rvalue reference argument, an rvalue reference argument is returned. However, my testing shows that the rvalue reference is turned into a lvalue reference by the universal reference function template. Why is it so?
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T>
T f1(T&&t) { //<-----this is a universal reference
cout << "is_lvalue reference:" << is_lvalue_reference<T>::value << endl;
cout << "is_rvalue reference:" << is_rvalue_reference<T>::value << endl;
cout << "is_reference:" << is_reference<T>::value << endl;
return t;
}
void f2(int&& t) {
cout << "f2 is_lvalue reference:" << is_lvalue_reference<decltype(t)>::value << endl;
cout << "f2 is_rvalue reference:" << is_rvalue_reference<decltype(t)>::value << endl;
cout << "f2 is_reference:" << is_reference<decltype(t)>::value << endl;
f1(t);
}
int main()
{
f2(5);
return 0;
}
In both GCC and VC++2010, this is the result:
f2 is_lvalue reference:0
f2 is_rvalue reference:1
f2 is_reference:1
is_lvalue reference:1
is_rvalue reference:0
is_reference:1
In other words, the parameter t
in f2
was an rvalue reference, but when passed to f1
, the parameter became a lvalue reference. Shouldn't it retain the rvalue-ness in f1
?
回答1:
The reason is that named rvalue references are treated as lvalues.
You should use std::move inside f2 when passing t to f1 to retain rvalueness:
void f2(int&& t) {
f1(std::move(t));
}
Here you can find a good explanation.
回答2:
Calling f1(t)
, the argument is the expression t
. Not static_cast<decltype(t)>(t)
or something. Your examination of decltype(t)
has nothing to do with the call of f1(t)
.
The expression t
has type int
and value category lvalue. (A rule of thumb is that if you can take the address of an expression then it is an lvalue, and you can certainly write &t
). The "information" that a reference variable was originally declared as a reference is only visible via a decltype
examination.
Since f1
is called with an lvalue, T
is deduced to int&
.
NB. You possibly want f1
to also use decltype(t)
rather than T
, if you ever want to see is_rvalue_reference
being true in f1
. For rvalue arguments, T
deduces to a non-reference type, e.g. if you fix f2
by making it do f1(std::move(t));
then f1
's T
is int
and decltype(t)
in f1
is int&&
.
回答3:
After studying the C++11 standard, I have a vague idea on what was going on after my f1(t);
in f2
. I describe it here to see if I got it right:
- In
f2
,t
is an lvalue of typeint&&
(notint
, this is an important difference) the call
f1(t);
causes type to be deduced like this:2.1 when
T
inf1
is given an lvalue, it is deduced as a reference of that lvalue's type orint&& &
2.2 reference collapsing causes
int&& &
to becomeint &
. This is the value ofT
.Since the parameter of
f1
is declared asT&&
, the type of parametert
inf1
isint & &&
. So reference collapsing occurs a second time to deduce the type oft
asint &
.Hence T=
int &
and type of parametert
isint &
. i.e. parametert
is an lvalue of typeint &
Any comment?
来源:https://stackoverflow.com/questions/41360880/why-an-rvalue-reference-is-turned-into-lvalue-reference-by-a-universal-reference