- 浏览: 261656 次
- 性别:
- 来自: 武汉
文章分类
最新评论
-
daknife:
谢谢你的这篇文章,让我大概了解了select的一部分底层原理。 ...
Linux-2.6.25 select系统调用源码分析 -
gjlzjb:
非常有用,谢谢哈。另外问下,您是否用过Pheonix Syst ...
Why Map/Reduce? -
zhangyafei_kimi:
canbo 写道请问,我怎么生成安装包,提供给其它用户安装呢? ...
下载最新的Google Chrome源码并编译 -
canbo:
请问,我怎么生成安装包,提供给其它用户安装呢?
下载最新的Google Chrome源码并编译
本系列全部转载自kuibyshev.bokee.com
1. 模板元编程的原理
1.1. 函数式编程
1.1.1. 函数式编程的概念
从上面的例子可以看出,由于模板元编程借助的是C++模板的特化能力,使它的设计方法迥异于普通的C++编程习惯。比如我们不能够在元函数中使用变量,编译期显然只可能接受静态定义的常量,也就因此,我们不能够使用传统意义上的循环;要实现任何分支选择,唯一的方法是调用其它元函数或者使用递归。这种编程风格正是具有重要理论意义的函数式编程(Functional Programming)。
Kenneth C. Louden指出函数式编程有如下的特点:
所有的过程都是函数,并且严格地区分输入值(参数)和输出值(结果)。
没有变量或赋值(变量被参数替代)。
没有循环(循环杯递归调用替代)。
函数的值只取决于它的参数的值而与求得参数值的先后或者调用函数的路径无关。
函数是一类值。
上述的最后一点是一个很重要的性质。所谓“函数是一类值(First Class Value)”指的是函数和值是同等的概念,一个函数可以作为另外一个函数的参数,也可以作为值使用。如果函数可以作为一类值使用,那么我们就可以写出一些函数,使得这些函数接受其它函数作为参数并返回另外一个函数。比如定义了f和g两个函数,用compose(f,g)的风格就可以生成另外一个函数,使得这个函数执行f(g(x))的操作,则可称compose为高阶函数(Higher-order Function)。
1.1.2. 函数式编程在C++中的常用方法
如果排除上述的最后一点,那么C语言已经能完整模拟出函数式编程的风格,但是在C语言中,函数却并不能作为一类值。也许我们会想到函数指针,但是试想如果我们的函数需要返回另一个函数的指针:
typedef int(*IntProc) (int);
IntProc compose(IntProc f, IntProc g)
{
int tempProc(int x)
{
return f(g(x));
}
return tempProc;
}
这个例子是不能通过编译的,因为C语言禁止在函数中定义函数。
幸运的是,我们可以在C++中利用类和模板来解决这个问题,因为C++允许定义()操作符,建立所谓的仿函数(Functor)。所以对象既可以作为值来传递和调用,又可以像函数一样用obj(x)的语法来使用了;另一方面,利用模板对返回值的控制,就可以避免上面无法定义内部函数的矛盾了。例如在GCC的 STL中有一个不属于C++标准的compose1函数,可以接受两个定义了()操作符的对象作为函数参数,并返回一个能进行f(g(x))运算的对象:
template <class Operation1, class Operation2>
class unary_compose
: public unary_function<typename Operation2::argument_type,
typename Operation1::result_type> {
protected:
Operation1 op1;
Operation2 op2;
public:
unary_compose(const Operation1& x, const Operation2& y) :
op1(x), op2(y) {}
typename Operation1::result_type
operator()(const typename Operation2::argument_type& x) const {
return op1(op2(x));
}
};
template <class Operation1, class Operation2>
inline unary_compose<Operation1, Operation2>
compose1(const Operation1& op1, const Operation2& op2) {
return unary_compose<Operation1, Operation2>(op1, op2);
}
1.1.3. 模板元编程中的高阶函数
那么,使用C++的模板机制又是否可以满足元函数作为一类值使用呢?答案是肯定的,不过解答稍稍有点复杂,并不像上述compose1的解决方法一样优雅。
#include <iostream>
using namespace std;
template<int n>
struct f
{
static const int value=n+1;
};
template <int n>
struct g
{
static const int value=n+2;
};
template <template <int n> class op1, template <int n> class op2>
struct compose
{
template <int x>
struct return_type
{
static const int value=op1<op2<x>::value>::value;
};
};
int main()
{
typedef compose<f, g>::return_type<6> h;
cout<<h::value<<std::endl; //6+2+1=9
}
在这里,f和g是两个元函数,compose接受f和g作为参数,生成了一个可以计算f(g(x))的新函数,看起来能够得出正确的答案,但是却仍然有两个问题。
首先,在compose的模板参数中,不能直接使用类似于template <class op1, class op2>的写法,原因是C++的模板机制严格区分模板和类,我们无法把模板直接作为另一个模板的参数,唯一可行的方法是使用“作为类模板的模板参数(Class Template Template Parameter)”,这样就把f和g的参数类型限制死了。不过我们似乎仍然可以勉强接受这个限制,事实上模板机制对非类型的模板参数本来就存在着限制,现在的C++标准禁止浮点数、类对象和内部链接对象(比如字符串和全局指针)作为模板参数,所以非类型的模板参数实际上只剩下布尔型和整型可用,写成类似composeint和composebool 的两个类仍然有可行性(模板参数是无法重载的)。
其次,同样是C++的模板机制严格区分模板和类的缘故,返回值return_type是一个模板而并不是一个元函数(或者说是类)。
上述两点都隐含着一个最大共同问题,C++对“作为类模板的模板参数”作了很严格的限制,所以一旦定义了以后,其模板参数的个数不能改变。当然,在STL里面我们似乎习惯了这个限制并用重新定义函数的方式来避开这个限制。但在作为函数式编程的模板元编程里面,似乎应该要求以更优雅的方式来解决(事实上即使是常规编程下的高阶函数,也有函数库提供了更好的组合方式[5])。
现在我们仅仅用到了模板元编程的数值计算能力,还没有引入它对类型的处理能力,稍后在分析MPL时会重新讨论到这个问题,还会显示出高阶函数更大的使用障碍。幸而MPL提供了很好的解决方案,通过增加包装层和使用lambda演算的概念,高阶函数依然能用上,使得模板元编程能够符合函数式编程的要求。
Krzysztof Czarnecki曾利用模板元编程实现了一个很简单的LISP,而LISP就是典型的函数式编程语言。
总之既然C++模板能够使用函数式编程,那么也就意味着这个C++语言的子集已经是图灵完备的,因为任何计算都可以使用函数来描述,理论上模板元编程应该能完成图灵机上的一切运算。
当然,理论上的完备并不意味着实用性。尽管在C++中能够在某种程度上使用函数式编程的风格,但是从实用性和效率来说,大概没有程序员使用纯粹的函数编程方式。不过,在进行模板元编程的时候,由于语法的特殊性,却不得不使用纯粹函数式编程。因此,模板元编程与传统的C++命令式编程相差很大,并不符合大多数C++程序员的习惯,不但会带来编写上的困难,还增加了许多程序理解上的难度。那么,为什么要使用模板元编程呢?首先我们应当了解模板元编程能够做些什么,其次,模板元编程有可能用在什么地方。
发表评论
-
The Elements of Programing Style
2009-08-09 18:26 1235把代码写清楚,别耍小聪明。 想干什么,讲的简单点、直接点。 只 ... -
6个变态的C语言Hello World程序
2009-06-01 09:37 807转载自:http://cocre.com/?p=914 下面 ... -
在VS2005中使用IBM Purify的注意事项
2009-05-12 12:24 3929Rational Purify 使用及分析实例可以见这里htt ... -
boost.pool源码整理和使用说明
2007-07-22 13:49 270Source #ifndef __KIMI_BOOST_PO ... -
一个STL风格的动态二维数组
2007-07-22 18:05 1492#ifndef __KIMI_BOOST_ARRAY2#def ... -
boost.any源码整理和使用说明
2007-08-24 22:44 1994Source #include <algorithm& ... -
boost.array源码整理和使用说明
2007-08-24 22:45 1217Source #include <cstddef> ... -
boost.BOOST_STATIC_ASSERT源码整理和使用说明
2007-08-24 22:49 1307Source #include <boost/conf ... -
boost.shared_ptr源码整理和使用说明
2007-08-24 22:51 4145Source #pragma once //share ... -
boost.lexical_cast源码整理和使用说明
2007-08-24 22:55 1478Source #include <cstddef> ... -
编译期判断类的继承性
2007-08-24 23:00 1069介绍一个雕虫小技:编译期判断类的继承性。具体来说就是类型U是否 ... -
boost.type_traits源码整理和使用说明(1)
2007-08-28 01:35 2028Introduction The Boost type-tr ... -
泛型快速排序
2007-08-28 03:20 903Source #ifndef kimi_quicksort ... -
C++ Meta Programming 和 Boost MPL(1)
2007-08-30 23:01 1559本系列全部转载自kuibyshev.bokee.com ... -
C++ Meta Programming 和 Boost MPL(3)
2007-08-30 23:06 1459本系列全部转载自kuibyshev.bokee.com ... -
C++ Meta Programming 和 Boost MPL(4)
2007-08-30 23:07 1632本系列全部转载自kuibyshev.bokee.com ... -
泛型归并排序
2007-09-18 00:23 1182#define SENTINEL_CARD (-1) # ... -
泛型插入排序
2007-09-18 00:25 1172#pragma once #include <iter ... -
boost.tuple源码整理和使用说明
2007-10-07 23:13 1536Introduction A tuple (or n-tup ... -
才发现VC中也可以检测内存泄漏
2009-03-30 14:54 1340#include <stdio.h> ...
相关推荐
C++ Metaprogramming 和 Boost MPL C++ 模板元编程的一些要点
2004年的英文文章,对Boost C++ 模板元编程的入门介绍,包括使用示例。
Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。 Boost中比较有...
Boost程序库探秘 深度解析C++准标准库第2版(501-640)_part3
用C++可变模板参数实现的一个类型容器,该代码只在GCC4.4上编译过,其他编译器可以不支持,使用者需要GCC4.4以上编译器,可以到GCC官网上下载
Boost程序库探秘 深度解析C++准标准库(501-550) (第二版),只有我这里有全套的,赶紧来下载吧! (501-550)指页数
Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,其中有些内容有望成为下一代C++标准库内容。在C++社区中影响...
STM32平台的MPL3115A2气压传感器的应用.pdf
主要介绍Freescale的MPL3115A2气压传感器的性能、内部寄存器设置、工作模式配置等,给出基于STM32的MPL3115A2气压测量系统的应用实例。
Boost 库涵盖的范围极广,有字符串和文本处理相关子库比如 format 库和 regexp 库,有容器相关子库比如 variant 库(和 Qt 的 QVariant 有得一拼),有迭代器子库比如 tokenizer 库(可以把字符进行 tokenize),...
C51和STC15用I2C操作MPL3115A2串口显示,测量高度和温度.ct1668显示. #include #include #include "ct1668.h" #define uchar unsigned char sbit SCL=P1^0; sbit SDA=P1^1;
MPU9150和MPU9250中MPL的用法,官方资料。
高清文字版,英文版,template、metatemplate学习。。
mpl3115.c - Support for Freescale MPL3115A2 pressure temperature sensor.
这是一个讲述C++模板高级应用的书籍,主要参考了《C++设计新思维》以及《产生式编程》两本书,同时也参考了BOOST的思想,对C++模板的应用自认为达到了非常的高度,其中也蕴含了非常先进的思想:“像编写程序一样来...
用于传感器 MPL115A2 的 Raspberry Pi C 驱动程序和 Python 绑定。 示例用法 C语言 # include " mpl115a2.h " # include # include int main ( int argc, char **argv){ char *i2c_device = " /dev/i2c-2 " ; ...
mpl3115_i2c_sensorhandle_t mpl3115Driver; BOARD_InitPins(); BOARD_BootClockRUN(); BOARD_InitDebugConsole(); PRINTF("\r\n ISSDK MPL3115 sensor driver example demonstration with po
与 Boost.MPL 不同的是,这个包利用了 C++11 特性,从而产生了更小的代码库。 主要特点: 广泛的算法库,适用于可变参数模板和(可能是用户定义的)模板类型(如std::tuple )。 编译时容器,例如map 、 vector和...