简体   繁体   中英

Using std::forward with a non-forwarding, plain old reference

Asking for a friend:

Why does std::forward in the following code cast the parameter c to an rvalue ?

template <typename T>
void f (T& c) {
    using value_type = typename std::remove_reference_t<T>; 
    std::vector<value_type> v; 
    // debugger reveals: push_back( T&& value ) is called here  
    v.push_back(std::forward<T>(c));
}

Note that c is not a universal/forwarding reference here. I am aware of the fact that this function would most probably be of more use if it actually was, but curious all the same.

std::forward<T>(c) is equivalent to static_cast<T&&>(c) .

If T&& is a forwarding reference then this allows an lvalue to be forwarded as an lvalue because T will be deduced as an lvalue reference type and T&& will be the same lvalue reference type by the reference-collapsing rules. In your situation, T&& is not a forwarding reference, so this doesn't work.

To understand this situation, you have to understand why forwarding references work. Given a definition like

template <typename T>
void foo(T&& t) {}

when you write something like

some_type some_object;
foo(some_object);

template deduction deduces T to be some_type& . Now the parameter t has the type some_type& && . Since you can't have references to references, reference collapsing rules are applied and some_type& && is collapsed to some_type& .

If, instead, you write something like

some_type some_object;
foo(std::move(some_object));

template deduction deduces T to be some_type . Now the parameter t has the type some_type&& . That's a perfectly valid type, so no reference collapsing is done.

Now we get to std::forward . All std::forward<U> does is cast its parameter to U&& . If U is some_type , as in the second case above, the parameter is cast to some_type&& . It remains an rvalue-reference. If U is some_type& , as in the first case above, reference collapsing is performed again, and some_type& && becomes some_type& . So std::forward returns an lvalue-reference.

So the ultimate answer to your original question is that the return type of std::forward only depends on the type passed as std::forward 's template parameter. Since T in your case will always be deduced as a non-reference type, std::forward will always return an rvalue-reference.

Well, the definition of std::forward<T>(x) is to cast x to type T&& . If you pass a non-reference as argument you'll get an rvalue reference T&& back. Since your T cannot be a reference type (you cannot have a reference to a reference), it must be a non-reference type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM