介绍
-
move
就是直接将对象强转为右值 -
forward
类型完美转发保持接受到的变量的原始引用类型(右值引用本身是左值)
move和forward使用案例
修改前面自己实现带空间配置器的vector
,加入接收右值的Allocator::construct
函数和push_back
函数:
void vector::push_back(T&& val)
{
if (full())
{
expand();
}
//*_last++ = val;
_allocator.construct(_last, val);
_last++;
}
template<typename T>
struct Allocator
{
void construct(T* p, const T& val) // 对象构造
{
new (p) T(val); // placement new
}
void construct(T* p, T&& val) // 对象构造
{
new (p) T(val);
}
};
int main(void)
{
CMyString str1 = "aaa";
vector<CMyString> vec;
cout << "----------------------------------" << endl;
vec.push_back(str1);
vec.push_back(CMyString("nnn"));
cout << "----------------------------------" << endl;
}
执行后结果为
CMyString(const char*)
----------------------------------
CMyString(const CMyString&)
CMyString(const char*)
CMyString(const CMyString&) // 没调用右值版本构造
~CMyString
----------------------------------
~CMyString
~CMyString
~CMyString
可以发现没有调用对应右值的构造,是因为用push_back
用右值引用接收右值后,这个右值引用本身有名字就又变为左值了,所以转发调用construct
时仍是调用左值版本进而调用左值引用版本的CMyString
构造函数
解决方法一:转调用时都要使用std::move,把右值引用转成右值
void construct(T* p, T&& val) // 对象构造
{
new (p) T(std::move(val)); // placement new
}
void push_back(T&& val)
{
if (full())
{
expand();
}
_allocator.construct(_last, std::move(val));//
_last++;
}
解决方法二
使用模板、std::forward完美转发重写push_back
和分配器的construct
函数
//引用折叠: & + && = &; && + && = &&
template<typename Ty>
void push_back(Ty&& val) // 函数模板类型推演 + 引用折叠(参数类型设置为右值引用),使得左值和右值都能调用这个模板函数
{
if (full())
{
expand();
}
// forward完美转发,能复原val原本类型,即右值引用-》右值;左值引用-》左值
_allocator.construct(_last, std::forward<Ty>(val));
_last++;
}
template<typename Ty>
void construct(T* p, Ty&& val) // 对象构造
{
new (p) T(std::forward<Ty>(val)); // placement new
}
如此改造就不需要左值引用和右值引用版本的函数各写一份