原文:https://blog.csdn.net/kupepoem/article/details/119948044
一、右值引用
int和int&是什么?都是类型。int是整数类型,int&则是整数引用类型。同样int&&也是一个类型。两个引号&&是C++ 11提出的一个新的引用类型,右值引用类型,这是一个新的类型。如果你记住这个新类型,那么很多疑问都能迎刃而解。void G(A &&a) ,就很容易明白a是新类型右值引用的一个变量一个值,肯定是一个左值而不是右值。
二、forward源码分析
/**
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }
/**
* @brief Forward an rvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp&&>(__t);
}
对于forward函数
std::forward<T>(u)有两个参数:T与 u。
a. 当T为左值引用类型时,u将被转换为T类型的左值。比如T 为int&
b. 否则u将被转换为T类型右值。比如T为 int ,int &&
三、示例分析
1、不使用 std::forward
下述代码G不管传入什么类型的参数,只会最终调用 void F(int& a);
using namespace std;
void F(int& a) {
cout << "int& version " <<a <<endl;
}
void F(int&& a) {
// do something
cout << "int&& version " <<a <<endl;
}
template<class A>
void G(A &&a) {
F(a);
}
int main (int argc, char *argv[]) {
int i = 2;
G(i); //输出int& version
G(5); //输出int& version
return 0;
}
G不管传入什么类型的参数,只会最终调用 void F(int& a)
这是为什么?
调用G(5)时,A被推导为int类型,但在G内部调用F(a)时,因为G函数中a是一个int &&的变量是个左值,调用F时编译译器自动推断调用 F(int & &&)类型的函数,根据引用折叠原理就会调用F(int & )c++引用折叠
调用G(i)时,A被推导为int &类型,根据引用折叠原理就会调用F(int & )。
2、用 std::forward
用 std::forward时,G的最终调用出现差异化
using namespace std;
void F(int& a) {
cout << "int& version " <<a <<endl;
}
void F(int&& a) {
// do something
cout << "int&& version " <<a <<endl;
}
template<class A>
void G(A &&a) {
F(std::forward<A>(a));
}
int main (int argc, char *argv[]) {
int i = 2;
G(i); //输出int& version
G(5); //输出int&& version
return 0;
}
调用G(5)时,A被推导为int类型,但在G内部调用F(a)时,因为G函数中a是一个int &&的变量是个左值,调用std::forward<A>(a)(std::forward<int>(a)),A此时为int,强制转换为了右值。
调用G(i)时,A被推导为int &类型,调用std::forward<A>(a)(std::forward<int&>(a)),还是强制转换成了左值。
四、std::move源码
头文件move.h
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
可以看出std::move也是进行了强制类型转换,把一个左值强制转换成右值,没有新奇之处,参数__t的类型是万能引用(CSDN),可以接受不同类型的参数。
using namespace std;
void F(int& a) {
cout << "int& version " <<a <<endl;
}
void F(int&& a) {
// do something
cout << "int&& version " <<a <<endl;
}
template<class A>
void G(A &&a) {
F(std::forward<A>(a));
}
int main (int argc, char *argv[]) {
int i = 2;
G(std::move(i)); //输出int&& version
G(5); //输出int&& version
return 0;
}
std::remove_reference
remove_reference源码
实现方式
这里的实现非常简单,就是去掉了类型的左值引用和右值引用,直接返回类型的基本内容。
性能开销
是否只在编译期间出现不会有任何运行时开销:是的。
/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template<typename _Tp>
struct remove_reference < _Tp&& >
{ typedef _Tp type; };
网友评论