Why does std :: forward convert lvalues and rvalues to rvalues?
I suppose I am confused with std::forward
. My function that it uses is std::forward
as follows, but it has been simplified and modified to make it easier to explain.
// This is an example code to explain my question simply.
template <typename Element>
void add(Element&& element) {
static std::vector vec;
vec.push_back(std::forward<Element>(element));
}
I tried two cases with the function above; Argument 1 of argument 1 and argument value of argument 2.
Case 1: lvalue argument
auto some_class = SomeClass();
add(some_class);
Case 2: value argument
add(SomeClass());
In the debugger, both cases pass the same next parts, part std::forward
and std::vector
.
std::forward
part:
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
std::vector
part:
#if __cplusplus >= 201103L
void
push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
The std::forward
part seems to convert both cases to rvalue reference &&
, because it uses static_cast<_Tp&&>
. And it is std::vector
treated as an element as an rvalue reference because it uses std::move()
.
I expected the Case 1 extension to be an lvalue because it has its own name and Case 2 to be an rvalue because it doesn't have its own name. I also expected to std::forward
convert case 1 to lvalue reference and register 2 to rvalue. Do lvalues, rvalues and values agree with my understanding std::forward
? If so, why std::forward
converts both values as the value of an rvalue, &&
.
If I have made a mistake, I am sorry for taking the time.
source to share
why
std::forward
converts as rvalue
Do not do it. According to the forwarder rule , when an lvalue is passed to add
, the template type argument Element
will be inferred as SomeClass&
. Then will be called std::forward<SomeClass&>(element)
and creation std::forward
will be
// before reference collapsing
constexpr SomeClass& &&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass& &&>(__t); }
and
// after reference collapsing
constexpr SomeClass&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass&>(__t); }
So, for the 1st case, std::forward
will return an lvalue. The lvalue-reference returned by the function is an lvalue.
BTW, for the second case, the templare argument Element
will be output as SomeClass
, then you can make the same output as above, finally the creation std::forward
will be
constexpr SomeClass&&
forward(SomeClass& __t) noexcept
{ return static_cast<SomeClass&&>(__t); }
The rvalue reference returned by funtion is an rvalue.
The result you get seems strange, for the 1st case you need to call std::vector::push_back(const T&)
. (I tried mcve , here )
source to share
The part you're missing is link folding. When passed to an lvalue, it will be of type T&
(or const T&
) for some T
. If you add this to your template forward
, you get:
return static_cast<T& &&>(__t);
Because of the link collapsing rules, this collapses to T&
.
Effective modern C ++ covers this in Item 28. Basically:
- Lvalues of type T are inferred as
T&
. - Values of type T are output as
T
.
With this and the link dropping rules above, hopefully you can figure out how it works std::forward
.
source to share