(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu) 参考:https://www.cnblogs.com/xiaobingqianrui/p/9064260.html
引言
之前总看到std::move, std::forward提供右值引用,但原理一直没有去细研究过; 想当然以为是std::move/std::forward内部识别了stl库的几种类型,提供的内部参数转移;
不过,在stl头文件看到了_Ty&&,细查一下,才发现这个是一个程序与编译器合力做的优化,并非是一个单纯编译器或程序级的优化;
如果没有定义右值引用相关的处理函数,用std::move/std::forward,全当是没用;
如果定义了右值引用相关的处理函数,没有做右值引用类型的转换,也全当是没用;
同时定义了右值引用相关的处理函数,同时调用函数时,参数做了右值引用类型转换,这样才可以用到右值引用函数的处理。
所以来说,觉得右值引用是一个编译器与程序合力配合的一个优化。
右值引用
在做变量复制或赋值的时候,指针类型转给另一个,而不进行复制的理念一直是有的;
不过对于语法层面支持,在stl库中添加容器的支持转移支持,是在c++11之后通过&&扩展用法支持的。
关于stl右值引用使用:
- 对于stl的相关类型的话,例如std::string,都是已经定义了右值引用的处理函数的,直接做参数右值类型转换使用就可以了。
- 结构体内带了std::string,std::vecotr变量的,结构体虽然没有定义右值引用处理函数;但赋值时使用了右值引用,编译器对结构体内变量仍会做右值引用赋值处理;
&&之前是代表逻辑运算的与; 不过c++11之后,在与变量类型放一起时_Ty&& 时用来识别右值引用; &&由程序中来支援编译器右值引用的识别;
样例
例如程序这么写:即提供右值引用copy,也提供普通copy struct Data { void* pData; char type; size_t size; Data() { Reset(); } void TryFree() { if (pData != nullptr) { delete pData; Reset(); } } void Reset() { pData = nullptr; size = 0; type = 0; } };
void CopyData(Data& dest, Data&& src) { printf(“Copy data with right reference\n”); dest.TryFree(); dest = src; src.Reset(); };
void CopyData(Data& dest, const Data& src) { printf(“Copy data normal\n”); dest.TryFree(); dest.pData = new char[src.size]; memcpy(dest.pData, src.pData, src.size); dest.size = src.size; dest.type = src.type; };
下面的方法可以使用到右值引用的函数处理:
// 调用时这样调用 CopyData(dest, std::move(src));
// 也可以这样调用 CopyData(dest, std::forward(src));
// 还可以这样调用 CopyData(dest, static_cast<Data&&>(src));
// 还可以这样调用 CopyData(dest, (Data&&)(src));
// 还可以这样调用,像std::move一样带remove_reference,为stl库提供引用去除 static_cast<std::remove_reference_t&&>(src);
代码解析
从上面可以看出,核心处理是类型转换为(_Ty&&)右值引用类型,这个类型由编译器识别的,代码就看不到了;
不过std::move,std::forward的代码可以看一下,一看究竟,是怎么转成&&类型的;
std::move不但做了类型转换,还附带做了针对stl添加转换类型remove_reference_t: // FUNCTION TEMPLATE move template_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable return (static_cast<remove_reference_t<_Ty>&&>(_Arg)); }
std::forward做的类型转换,转换为右值引用类型,和自己写类型转换也是一样的: // FUNCTION TEMPLATE forward template _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue return (static_cast<_Ty&&>(_Arg)); } template _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue static_assert(!is_lvalue_reference_v<_Ty>, “bad forward call”); return (static_cast<_Ty&&>(_Arg)); }
看完发现,不光做了强制类型转换,还做了stl库的remove_reference_t引用类型识别; 从这个角度来说 a. stl库类型的右值引用,使用std::move/std::forward比较保险一点; b. 如果自定义类型的话,就随便了,强制转换也好,使用std::move/forward也好,都ok。
(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)
|