M
M
Max Goncharenko2018-07-16 14:16:55
C++ / C#
Max Goncharenko, 2018-07-16 14:16:55

Why would std::forward have two signatures and explicitly state the type?

In the standard, std::forward has two signatures (for lvalue and rvalue) and requires the user to explicitly specify the type:

template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;

At a time when a type can be deduced and implemented with just one signature. static_castLink folding will work and the result will be equivalent to the standard function .
template <typename T>
decltype(auto) my_forward(T&& arg) // universal reference (can be rvalue- or lvalue-reference)
{
    return static_cast<T&&>(arg);
    // T&& can be expanded to:
    // Foo& && - gives us Foo&
    // const Foo& && - give us const Foo&
    // Foo && - gives us Foo&&
    // const Foo && - gives us const Foo&&
}

Usage Comparison:
template <typename T>
func (T&& foo1, T&& foo2)
{
    f(std::forward<Foo>(foo1), my_forward(foo2));
}

Answer the question

In order to leave comments, you need to log in

3 answer(s)
E
Evgeny Shatunov, 2018-07-16
@reverse_kacejot

I think this article will answer your questions.
If the most pointwise, then this is the section "Implementing the ideal transfer using std::forward" and a quote in it.

Another point to note is the use of std::remove_reference. In fact, forward can be implemented without using this function. Reference compression will do all the work, so using std::remove_reference is redundant for this. However, this function allows you to infer T& t in situations where this type cannot be inferred (according to the C++ standard, 14.8.2.5), so you must explicitly specify template parameters when calling std::forward.

A
Alexey Makarenya, 2018-07-16
@makarenya

Attempt No. 2, the first one was nonsense:
The main problem is that inside the method all parameters are of the l-value type. That is, the parameter modifier && is only a requirement that the calling code pass r-value in place of this argument (and for templates it doesn’t even require this, thanks to link gluing). And if so, then your my_forward method will remake it to return l-value references in general for all arguments.
Hence the need to explicitly specify the type of the argument is taken - so that it is the derived type that is used, and not l-value for this type. Well, why 2 specializations: if you explicitly specify the type of the argument and explicitly specialize the function with some r-value, then my_forward will expect strictly r-value, and the parameter value that is l-value will not fit there.
To explain my thoughts, code

#include <iostream>

template<typename T>
decltype(auto) my_forward(T&& arg)
{
    return static_cast<T&&>(arg);
}

template <typename T, typename K>
void forward(T&& t, K&& k)
{
    std::cout << std::is_same<int&&, decltype(my_forward(t))>::value << std::endl;
    std::cout << std::is_same<int&, decltype(my_forward(k))>::value << std::endl;
    std::cout << std::is_same<int&&, decltype(std::forward<T>(t))>::value << std::endl;
    std::cout << std::is_same<int&, decltype(std::forward<K>(t))>::value << std::endl;
}

int main()
{
    int lval = 10;
    forward(10, lval);
    return 0;
}

will return
0
1
1
1
That is, the assumption that my_forward returned an int&& for the first parameter is not true!

M
Mercury13, 2018-07-16
@Mercury13

std::forward is for use in templates; without a template, you can use the good old std::move.
In addition, std::forward is set up in such a way that the template parameter must be specified explicitly.
The task of std::forward is to pass further a simple or temporary reference, depending on which reference the user passes.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question