C++11:利用模板简化重载右值引用参数的函数

版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net/10km/article/details/50827943

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>中的模板函数,详细说明请打开链接查看。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数值分析与有限元编程

如何在Intel官网申请Fortran编译器的License

Intel Parallel Studio XE 有三种版本:Composer Edition,Professional Edition和Cluster Edi...

66820
来自专栏程序猿声

代码 | 自适应大邻域搜索系列之(2) - ALNS算法主逻辑结构解析

在上一篇推文中,教大家利用了ALNS的lib库求解了一个TSP问题作为实例。不知道你萌把代码跑起来了没有。那么,今天咱们再接再厉。跑完代码以后,小编再给大家深入...

21750
来自专栏Java帮帮-微信公众号-技术文章全总结

听说你 “精通” C++???

我等这条短信等得太久了。想起这几个月求职的心酸、无助,再想想拜托了无数网友内推换来的是网友们的失望与笔试结果的石沉大海,看到了这条短信我简直快要哭了出来。

12220
来自专栏Java学习录

JVM的类加载机制

加载是第一阶段,因为此时的虚拟机中还没有class的相关信息,必须将class文件加载到虚拟机中才能进行接下来的操作。加载的过程主要分为以下3个步骤:

7410
来自专栏gojam技术备忘录

function*/生成器函数

function*语句允许你声明一个生成器函数,这种函数的返回值是一个Generator对象,它允许你控制函数的暂停、继续执行。这种同步操作允许我们使用Java...

13830
来自专栏技术探索

elk6.x 安装x-pack

参考: https://discuss.elastic.co/t/logstash-with-x-pack/90230 https://www.cnblogs....

46440
来自专栏Java架构筑基

面试题丨Java的类/实例初始化过程

昨天看到群里面有人分享了一道题目,我答错了,于是趁机了解了下Java的类/对象初始化过程:

14500
来自专栏程序猿声

自适应大邻域搜索代码系列之(1) - 使用ALNS代码框架求解TSP问题

上次出了邻域搜索的各种概念科普,尤其是LNS和ALNS的具体过程更是描述得一清二楚。不知道你萌都懂了吗?小编相信大家早就get到啦。不过有个别不愿意透露姓名的热...

14630
来自专栏java工会

为什么说 C 语言比 Java 难?

“小伙子,我看你骨骼惊奇,是万中无一的编程奇才,维护世界和平就靠你了,我这有本秘籍《Java编程思想》,见与你有缘,就50块买给你了!”

15120
来自专栏gojam技术备忘录

外边距折叠(Margin collapsing)笔记?

外边距折叠是指有时候上边距与下边距坍缩成较大的那一个边距的行为。它只会发生在同一BFC的块级元素间,并且永远不会发生在浮动元素或绝对定位元素间。

12530

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励