Replies
What is the difference?
In practice there is no difference, the expected result in the execution is the same, given that only the way of writing is being more verbose
You are just typedefining the T and using the type defined through the typename, meaning literally no difference.
Why is latter the usual implementation?
I didn't follow this update so I can't say if they used this code officially in their repository, it's probably someone else's implementation and not the official one, whatever the reason is this:
They chose to do it this way, even though both implementations are invalid for the real scenario.
Note that both have the same result and do not diverge at all in the execution, that is, in the real scenario, diverging between them would not cause any difference or run/compilation error or anything like that
Code review
Missed add "typename" for compilation to succeed
template<typename T>
struct identity {
typedef T type;
};
template<typename T>
T&& forward(typename identity<T>::type&& param)
{
return static_cast<**typename** identity<T>::type&&>(param);
}
Explanations
Why is invalid
Considering the below code, the argument T is "int" containing the return type is "int&&", together expecting a function parameter of type rvalue (int&&) but an lvalue int& was passed generating a compilation error
#include <iostream>
template<typename T>
T&& forward(T&& param)
{
return static_cast<T&&>(param);
}
int main() {
int value = 5;
forward<int>(value);
return 1;
}
Real scenery
The line 25, redir(5), causes a compilation error, and this would be a real scenario.
The error is because the template T referring to the void redir is of type int and when calling forward< int >(param) it is passing param which is an lvalue variable that was explained in the section Why is invalid
#include <iostream>
template<typename T>
T&& forward(T&& param)
{
return static_cast<T&&>(param);
}
void print(int &&value){
std::cout << "rvalue: " << value << std::endl;
}
void print(int &value){
std::cout << "lvalue: " << value << std::endl;
}
template <class T>
void redir(T &¶m){
print(forward<T>(param));
}
int main() {
int value = 5;
redir(value);
**redir(5);**
return 0;
}
Solution
Currently the code has been updated and the problem has been fixed, you can check it at: https://en.cppreference.com/w/cpp/utility/forward.
The code would be similar to this:
#include <iostream>
template< class T >
T&& forward( std::remove_reference_t<T> &¶m)
{
return static_cast<T&&>(param);
}
template< class T >
T&& forward( std::remove_reference_t<T> ¶m)
{
return static_cast<T&&>(param);
}
void print(int &&value){
std::cout << "rvalue: " << value << std::endl;
}
void print(int &value){
std::cout << "lvalue: " << value << std::endl;
}
template <class T>
void redir(T &¶m){
print(forward<T>(param));
}
int main() {
int value = 5;
redir(value);
redir(5);
return 0;
}
std::remove_reference_t is optional, even without using it the result will be the same.
They decided to use remove_reference_t due to good practices, that is, they are reinforcing that the first function expects int& and the second int&&.
Why std::remove_reference is irrelevant
Although it does not change the expected result in code execution, it is important due to good programming practices, it reinforces the expected result so to speak.
Well, but the result explanation doesn't change is due to the following conversion rules:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
As we can, the only way to obtain an rvalue (T&&) is through the conversion T&& + T&&, or T&& only.
Why is it necessary to use 2 functions (std::forward) and not just static_cast in one function
<int&&> int&& && forward(int&& &¶m) will result int&& forward(int&& param)
<int&> int& && forward(int& &¶m) will result int& forward(int& param)
<int> int && forward(int &¶m) will result int&& forward(int&& param)
Note that the second function: T&& forward( std::remove_reference_t ¶m) fills in just what was missing
<int> int && forward(int ¶m) will result int&& forward(int& param)
For this reason you need to declare 2 std::forward functions.