Expression Template(表达式模板,ET)
1.前言
在前一篇文章自己实现简单的string类中提到在实现+操作符重载函数时,为了防止返回时生成的临时对象调用拷贝构造函数动态申请内存空间,使用了一个叫move的函数,它是C++0x新增的特性。既然是C++0x新增的特性,那么在以前没有这个特性的情况下,对于临时对象动态申请内存空间的问题是不是可以有其它的方法解决或避免呢?答案是肯定的,可以用Expression Template(表达式模板,ET)来解决。
2.表达式模板
对于前面的String类,我们可能经常会使用下面的表达式:
String str4 = str1 + str2 + str3;
这个表达式有一个问题,就是产生了“不必要”的临时对象。因为 str1 + str2 的结果会存放在一个临时对象 temp1上,然后temp1 + str3的结果会存放在令一个临时对象temp2上,temp2最后把结果传给str4 进行初始化。如果这些向量很长,或者表达式再加几节,不仅会产生很多的临时对象,而且要动态申请内存空间来存放String内部的字符串,这很明显是低效的。move函数解决了当+操作符重载函数返回临时对象时,编译器在栈中为其拷贝另一个对象的开销(由于+操作符重载函数内声明的临时对象temp1在函数的作用域外事无法使用的,所以编译器自动的在调用+操作符重载函数的域内声明了一个临时对象temp11,用以存放拷贝的temp1),但并没有解决产生临时对象的问题。表达式模板的思路很简单,就是对+操作进行推迟计算,即一次性计算所有的+操作,这样就不需要产生临时对象来保存+操作的临时结果。由于要推迟计算,所以必须保存str1,str2,str3操作数用于后面真正计算推迟的+操作。
3.基于String的表达式模板实现
虽然表达式模板的思路很简单,但其实现确不是想象的那么简单。原来的做法中,operator + 直接进行了计算,既然我们不想它“过早”的计算,那么我们就必须重新重载一个operator + 运算符,在这个运算中不进行真正的运算,只是生成一个对象,在这个对象中把加法运算符两边的操作数保留下来,然后让它参与到下一步的计算中去。(好吧,这个对象也是临时的,但它的代价非常非常小,因为它不需要动态申请内存空间,我们先不理会它)。
class String; template <typename L>
class ExpPlus {
const L &lstr;
const char *rstr;
const int _size;
public:
ExpPlus(const L & a_l, const char *a_r):lstr(a_l), rstr(a_r), _size(strlen(a_r))
{
}
ExpPlus(const L & a_l, const char &a_r):lstr(a_l), rstr(&a_r), _size()
{
}
ExpPlus(const L & a_l, const String &a_r):lstr(a_l), rstr(a_r.c_str()), _size(a_r.size())
{
}
void copy_str(char *d_str) const;
int size() const
{
return _size + lstr.size();
}
}; template <typename L>
void ExpPlus<L>::copy_str(char *d_str) const
{
lstr.copy_str(d_str);
strncpy(d_str + lstr.size(), rstr, _size);
}
//用于+模板
template <typename L>
ExpPlus<L> operator + (const L & a_l, const char & a_r) {
return ExpPlus<L>(a_l, a_r);
} template <typename L>
ExpPlus<L> operator + (const L & a_l, const char* a_r) {
return ExpPlus<L>(a_l, a_r);
} template <typename L>
ExpPlus<L> operator + (const L & a_l, const String & a_r) {
return ExpPlus<L>(a_l, a_r);
}
我们增加了一个模板类 ExpPlus,用它来代表加法计算的“表达式”,但在进行加法时,它本身并不进行真正的计算。对这个类,定义了copy_str函数,在这个函数中才进行了真正的字符串加法计算。当然,它除了支持保存String对象,还支持保存char和char *。因为String对象加字符串常量和字符也是很常见的,如下面的表达式:
String str2 = str1 + 'a' + "cd"
对于我们实现的String类,必须重新重载一个operator = 函数,用于一次性计算右边表达式的值。修改后的String代码如下:
class String
{
public:
。。。
template <typename Exp>
String& operator=(const Exp &a_r); //新加
。。。
void copy_str(char *d_str) const; //新加
}; void String::copy_str(char *d_str) const
{
strcpy(d_str, _string);
} template <typename Exp>
String& String::operator=(const Exp &a_r)
{
char *temp_str;
int size = a_r.size(); temp_str = new char[size + ];
a_r.copy_str(temp_str);
temp_str[size] = ;
if (_string)
delete _string;
_string = temp_str;
_size = size;
return *this;
}
上面只给出了新加的代码,其它代码在自己实现简单的string类中已经给出。
上面这段话,对于不了解ET的人来说,也许一时间还不容易明白,我们一步一步来:
在 str4 = str1 + str2 + str3这个式子中,首先遇到 str1 + str2,这时,模板函数 operator + 会被调用,这时只是生成一个临时的ExpPlus<String>对象(我们叫它 t1 吧),不做计算,只是保留计算的左右操作数(也就是str1和str2),接着,t1 + str3 ,再次调用同样的 operator + ,而且也只是生成一个对象(我们叫它 t2 吧),这个对象的类型是 ExpPlus<ExpPlus<String>>,同样,t2 在这里只是保留了两边的操作数(也就是 t1 和 str3)。直到整个表达式“做完”,没有任何东西进行了计算,所做的事情实际上只是用 ExpPlus 这个模板类把计算式的信息记录下来了(当然,这些信息就是参与计算的操作数)。
最后,当进行 str4 = t2 的时候,String的赋值运算符被调用(用 t2 作参数)。注意,这个调用中的语句a_r.copy_str(temp_str),实际是是调用t2.copy_str(temp_str),t2.copy_str函数中又调用t1.copy_str,t1又调用str1.copy_str,就这样一步一步地将str1,str2,str3中的字符串拷贝到temp_str中,最终得到str4 = str1 + str2 + str3。就像变“魔术”一样,我们通过ExpPlus完成了“延迟计算”,并避免了大型的 String临时对象的产生。
4.总结
表达式模板保持了表达式直观和效率两者,很强大,但很显然它太复杂,主要是作为类库的设计者的武器。另外,它也可能使得使用者要理解一些“新”东西,比如,如果我想存储表达式的中间值,那么 <ExpPlus<ExpPlus<...<String>...> 一定会让使用者理解半天。
参考:http://www.cnblogs.com/liyiwen/archive/2009/12/03/1616627.html
http://www.cppblog.com/kesalin/archive/2009/05/28/85983.html
Expression Template(表达式模板,ET)的更多相关文章
- C++ template —— 表达式模板(十)
表达式模板解决的问题是:对于一个数值数组类,它需要为基于整个数组对象的数值操作提供支持,如对数组求和或放大: Array<), y(); ... x = 1.2 * x + x * y; 对效率 ...
- MShadow中的表达式模板
表达式模板是Eigen.GSL和boost.uBLAS等高性能C++矩阵库的核心技术.本文基于MXNet给出的教程文档来阐述MXNet所依赖的高性能矩阵库MShadow背后的原理. 编写高效的机器学习 ...
- .NET平台开源项目速览(8)Expression Evaluator表达式计算组件使用
在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Expression Evaluator验证组件.那里只是概述了一下,并没有对其使用和强大功能做 ...
- Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx
Atitit.java expression fsm 表达式词法分析引擎 v2 qaa.docx C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\Java ...
- Atitit.java expression fsm 表达式分词fsm引擎
Atitit.java expression fsm 表达式分词fsm引擎 C:\0workspace\AtiPlatf_cms\src\com\attilax\fsm\JavaExpFsm.java ...
- IE css expression(表达式)
很多时候我们需要对IE6的bug写一些hack,如max-height,absolute元素高度100%等. css里面的 expression(表达式)和js里面的差不多,如: 获取当前元素的高度: ...
- art template前端模板引擎
偶然看到后台有一段代码 采用的是art template的模板引擎 地址为 http://aui.github.io/artTemplate/ 这段代码很简洁 var html = template( ...
- Template Method 模板设计模式
什么是模板设计模式 对于不了解的模板设计模式的来说,可以认为如同古代的造纸术一样,纸所以成型,取决于用了模板的形状,形状又由镂空的木板组成,而你想要造什么纸,又取决于你使用什么材料. 上面提到了两个关 ...
- Atitit.eclipse comment template注释模板
Atitit.eclipse comment template注释模板 1. Code templet1 1.1. Settpath1 1.2. 设置存储1 1.3. 导出设置1 2. Java d ...
随机推荐
- 为什么要用elasticsearch-理解加深中
首先的概念 基于Lucene 分布式实时文件存储 实时的分析搜索引擎 能达到实时搜索 优势的地方 1.横向可扩展性:只需要增加一台服务器,做一点儿配置,启动一下ES进程就可以并入集群: 2.分片机制提 ...
- Spring MVC篇一、搭建Spring MVC框架
本项目旨在搭建一个简单的Spring MVC框架,了解Spring MVC的基础配置等内容. 一.项目结构 本项目使用idea intellij创建,配合maven管理.整体的目录结构如图: 其中ja ...
- Myeclipse 不能保存汉字
window-->首选项-->content type-->Text-->Default encoding改为UTF-8,点击update
- MVC框架
MVC (Modal View Controler)本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用 ...
- dedecms功能性函数封装(XSS过滤、编码、浏览器XSS hack、字符操作函数)
dedecms虽然有诸多漏洞,但不可否认确实是一个很不错的内容管理系统(cms),其他也不乏很多功能实用性的函数,以下就部分列举,持续更新,不作过多说明.使用时需部分修改,你懂的 1.XSS过滤. f ...
- 06.DOM操作应用高级
获取表格tBodies.tHead.tFoot.rows获取行 cells获取td隔行变色 <!DOCTYPE HTML> <html> <head> <m ...
- ARM 编译 phddns
参考博文http://bluegemyf.blog.163.com/blog/static/11816340201310472751513/ 1.安装必要的开发包 sudo apt-get inst ...
- Proxy
Proxy: Script: http://apgutmg01:8080/array.dll?Get.Routing.Script Server: apgutmg01 Port: 8080
- 初识Windows程序
首先,我们创建第一个Windows程序,一共分为4个步骤: 1.打开Visual Studio开发工具 2.选择"文件"→"新建"→"项目" ...
- Surface Shader简单向导
Surface Shader 表面着色器 描述 当你的Material要处理光照,则一般使用Surface Shader.Surface Shader隐藏了光照的计算,你只需要在surf函数里设置好反 ...