C++11标准中引入了右值的概念,是个非常好的东东,使用得当可以大大减少对象间无谓的复制(关于右值,左值的概念请自行问度娘)。
左值引用版本和右值引用版本的函数
下面是
matrix_cl
类的两个重载的构造函数,这两个构造函数除了最后一个参数不同,其他的参数都完全一样,只有最后一个参数不同(分别为右值和左值引用)。
当调用该构造函数时,如果最后一个参数为右值引用的时候,会优先调用第一个构造函数,使用移动语义
std:move()
将
rv
转为右值,将
rv
的内容赋值给
this->v
,这时调用的是
std::vector
的移动赋值操作符
vector&operator=(vector&&)
,这样,
this->v
不会重新分配内存,而是直接使用
rv
的内存数据。
// 右值引用版本
matrix_cl(size_t width, size_t height, uint8_t align,std::vector<E> &&rv):matrix_cl(width,height,align)
{
throw_if(rv.size()&&get_row_stride()*height!=rv.size()) // 参数合法性检查,请无视
this->v=std::move(rv); // 移动语义
// 这里的=为移动赋值操作符std::vector& operator=(const vector&&)
};
// 左值引用版本
matrix_cl(size_t width, size_t height, uint8_t align,const std::vector<E> &lv):matrix_cl(width,height,align){
throw_if(lv.size()&&get_row_stride()*height!=lv.size())
this->v=lv;
// 这里的=为复制赋值操作符 std::vector& operator=(const vector&)
};
注:上面代码中模板参数E为类模板参数,请忽视,下同。
如果最后一个参数不是右值引用,则会调用第二个函数(左值引用版本),这时
this->v=lv;
调用的是
std::vector
的复制赋值操作符
vector&operator=(vector&)
,这样,
this->v
会重新分配内存将
lv
的内容复制一份。
能不能更简化?
这样看起来一切都挺完美。。。但是,好像哪里不对。。。
如果按照上面的路子,对于复杂类型的参数对象,都要分别提供左值和右值引用两个版本,才能分别针对右值和右值进行处理。。。。上面的例子中构造函数只有3行,还好办,如果构造函数有30行甚至更多的代码,我们岂不是要把这些代码几乎原样复制两个版本?如果真是这样的话,这代码的就太臃肿了,可维护性也不好啊,能不能将两个函数合并为一个?
yes!we can
如果要把上面两个函数合并为一个就要用到模板编程了。
下面是合并后的代码。
template<typename _V=std::vector<E>
,bool _RV=std::is_rvalue_reference<_V&&>::value // 模板常量参数,用于判断v是否为右值引用
>
matrix_cl(size_t width, size_t height, uint8_t align,_V &&v):matrix_cl(width,height,align){
throw_if(v.size()&&get_row_stride()*height!=v.size())
this->v=_RV?std::move(v):v;
};
这里用到了
#include <type_traits>
中的
std::is_rvalue_reference
来判断参数v的引用类型,
然后在函数体内根据
_RV
的值来决定调用
std::move
将v转为右值引用,还是直接赋值.
更严谨的写法
其实更严谨的写法,还应该为模板参数
_V
加上类型限制,代码如下
template<typename _V
,bool _RV=std::is_rvalue_reference<_V&&>::value // 模板常量参数,用于判断v是否为右值引用
,typename _ENABLE=typename std::enable_if<std::is_base_of<std::vector<E>,typename std::decay<_V>::type>::value>::type
// _ENABLE参数限制_V必须是std::vector<E>或其子类
>
matrix_cl(size_t width, size_t height, uint8_t align,_V &&v):matrix_cl(width,height,align){
throw_if(v.size()&&get_row_stride()*height!=v.size())
this->v=_RV?std::move(v):v;
};
有了
_ENABLE
进行参数类型限制,在类中有多个类型的模板构造函数的情况,调用构造函数时就不会将别的类型的参数误传入,而产生编译错误。
这里用到的
std::enable_if
,
std::is_base_of
,
std::decay
都是定义在
#include<type_traits>
中的模板函数,详细说明请打开链接查看。