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.

+3


source to share


2 answers


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 )

+1


source


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

.

+1


source







All Articles