关于完美转发std::forward的一点随笔

在c++11引入右值引用后,在使用模板传参调用其他函数会变得优雅很多,右值引用主要运用在两个方面,移动语义和完美转发。

今天记录一下完美转发函数中,被身边人问得最多的一个问题,也是非常基础简单的一个模板运用。

先看一段代码

void Show(int &t) {
    cout << "lvalue" << endl;
}

void Show(int &&t) {
    cout << "rvalue" << endl;
}

template<typename T>
void Test(T &&t) {
    Show(t);
    Show(std::forward<T>(t));
    Show(std::move(t));
}

int main() {
    Test(1); // lvalue rvalue rvalue
    int a = 1;
    Test(a); // lvalue lvalue rvalue
    Test(std::forward<int>(a)); // lvalue rvalue rvalue      ---这里为什么第二个值为右值
    Test(std::forward<int&>(a)); // lvalue lvalue rvalue
    Test(std::forward<int&&>(a)); // lvalue rvalue rvalue
    return 0;
}

这里比较有疑惑的点是 Test(std::forward<int>(a)); 第二个打印为什么是右值

也有人提出疑惑,作者也做了解答,但是还是有部分小伙伴搞不明白。

我们来一步一步分析一下

首先,需要先了解一下右值引用的折叠规则

  • 右值引用和右值引用叠加将得到右值引用;
  • 右值引用和左值引用叠加将得到左值引用;
  • 左值引用和左值引用叠加将得到左值引用.
template <typename T>
using TR = T&;
						   // v 的类型
TR<int> v;				 // int&
TR<int>& v;				// int&
TR<int>&& v;			   // int&
template <typename T>
using TRR = T&&;
						    // v 的类型
TRR<int> v;				 // int&&
TRR<int>& v;				// int&
TRR<int>&& v;			   // int&&

接下来看看std::forword源码

template <class _Ty>
_Ty&& forward(remove_reference_t<_Ty>& _Arg) 
{
    return static_cast<_Ty&&>(_Arg);
}

template <class _Ty>
_Ty&& forward(remove_reference_t<_Ty>&& _Arg)  
{
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

可以看到,函数中做了两件事

1. 根据参数_Arg类型进行函数重载,分别重载了左右值两个引用类型参数。(& _Arg  和  && _Arg)

1. 根据函数模板类型进行右值引用的折叠 ,然后静态转换为折叠后的类型( _Ty  与 && 折叠成_Type,然后static_cast<_Type>_Arg )。

所以,在这个模板函数中,我们得分清模板类型_Ty 和 模板参数 _Arg  是两个东西,_Ty决定转发后得类型, _Arg参数最后会强转成_Ty折叠后的类型。

ps:  转发后类型由std::forward<T>的T决定,和参数本身是否左右值没有直接关系。

现在,回过头再来看std::forward<int>(a),是不是就明白了,分两步:

        1.  模板类型折叠         ( int 和 && 折叠 ——> int&& )

         2. 参数引用类型转换    ( static_cast<int&&>a )

最后得到的不就是右值引用吗 = _=''

附一段UE中的转发例子

摘自UObjectBase.h

// 借助静态变量的初始时机特性,来收集反射信息
// 完美转发,接受模板参数转发
struct FRegisterCompiledInInfo
{
	template <typename ... Args>
	FRegisterCompiledInInfo(Args&& ... args)
	{
		RegisterCompiledInInfo(std::forward<Args>(args)...);
	}
};


摘自UObjectBase.cpp

//  完美转发的调用函数,用于注册类、结构体、枚举
void RegisterCompiledInInfo(const TCHAR* PackageName, const FClassRegisterCompiledInInfo* ClassInfo, size_t NumClassInfo, const FStructRegisterCompiledInInfo* StructInfo, size_t NumStructInfo, const FEnumRegisterCompiledInInfo* EnumInfo, size_t NumEnumInfo)
{
	LLM_SCOPE(ELLMTag::UObject);

	for (size_t Index = 0; Index < NumClassInfo; ++Index)
	{
		const FClassRegisterCompiledInInfo& Info = ClassInfo[Index];
		RegisterCompiledInInfo(Info.OuterRegister, Info.InnerRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
	}

	for (size_t Index = 0; Index < NumStructInfo; ++Index)
	{
		const FStructRegisterCompiledInInfo& Info = StructInfo[Index];
		RegisterCompiledInInfo(Info.OuterRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
		if (Info.CreateCppStructOps != nullptr)
		{
			UScriptStruct::DeferCppStructOps(FTopLevelAssetPath(FName(PackageName), FName(Info.Name)), (UScriptStruct::ICppStructOps*)Info.CreateCppStructOps());
		}
	}

	for (size_t Index = 0; Index < NumEnumInfo; ++Index)
	{
		const FEnumRegisterCompiledInInfo& Info = EnumInfo[Index];
		RegisterCompiledInInfo(Info.OuterRegister, PackageName, Info.Name, *Info.Info, Info.VersionInfo);
	}
}

调用:

// 自定义类MyClass, 全局静态用来收集此类的反射信息
static FRegisterCompiledInInfo Z_CompiledInDeferFile_FID_Users_My_Documents_Unreal_Projects_Hello_Source_Hello_MyClass_h_2899306851(TEXT("/Script/Hello"),
		Z_CompiledInDeferFile_FID_Users_My_Documents_Unreal_Projects_Hello_Source_Hello_MyClass_h_Statics::ClassInfo, UE_ARRAY_COUNT(Z_CompiledInDeferFile_FID_Users_My_Documents_Unreal_Projects_Hello_Source_Hello_MyClass_h_Statics::ClassInfo),
		nullptr, 0,
		nullptr, 0);

代码摘自: 

https://zhuanlan.zhihu.com/p/137662465

                   C++完美转发-优快云博客

                        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值