有限元方法编程C++模板元编程
有限元方法编程:C++模板元编程
疯狂代码 http://CrazyCoder.cn/ ĵ:http:/CrazyCoder.cn/VC/Article12962.html
如果你仅把它看作古怪特性而没有打动你那你就不会对它有足够深入研究
C 并不是为 template metaprogramming(模板元编程)设计但是自从 TMP 在 1990 年代早期被发现以来它已被证明非常有用使 TMP 变容易扩展很可能会被加入到语言和它标准库的中是TMP 是被发现而不是被发明TMP 所基于特性在 templates(模板)被加入 C 时候就已经被引进了所需要全部就是有人注意到它们能够以种精巧而且意想不到方式被使用
TMP 有两个强大力量首先它使得用其它思路方法很难或不可能些事情变得容易第 2 template
metaprograms(模板元)在 C 编译期间执行它们能将工作从运行时转移到编译时个结果就是通常在运行时才能被察觉能够在编译期间被发现另个结果是 C 使得 TMP 使用在以下每个方面都能更有效率:更小可执行代码更短运行时间更少内存需求(然而将工作从运行时转移到编译时个结果就是编译过程变得更长使用 TMP 可能比它们non-TMP 对等物占用长得多编译时间)
考虑STLadvance伪代码(在C箴言:为类型信息使用特征类中你现在可能需要读该文在本文中我假设你已经熟悉了该文内容)我突出表示代码中伪代码部分:
template
void advance(IterT& iter, DistT d)
{
(iter is a random access iterator) {
iter d; // use iterator arithmetic
} // for random access iters
{
(d >= 0) { while (d--) iter; } // use iterative calls to
{ while (d) --iter; } // or -- for other
} // iterator categories
}
我们可以用 typeid 把伪代码变成真正代码这就产生了个解决此问题“常规” C 思路方法——它全部工作都在运行时做:
template
void advance(IterT& iter, DistT d)
{
(typeid(typename std::iterator_traits::iterator_category)
typeid(std::random_access_iterator_tag)) {
iter d; // use iterator arithmetic
} // for random access iters
{
(d >= 0) { while (d--) iter; } // use iterative calls to
{ while (d) --iter; } // or -- for other
} // iterator categories
}
C箴言:为类型信息使用特征类中指出这个 typeid-based(基于 typeid)思路方法比使用 traits 思路方法效率低这个思路方法(1)类型检测发生在运行时而不是编译期(2)用来做运行时类型检测代码必须出现在可执行代码中实际上这个例子展示了 TMP 如何能比个“常规”C 更高效 traits 思路方法是 TMP记住traits 允许编译时在类型上 ... 计算
我先前谈及些事情在 TMP 中比在“常规”C 中更简单而 advance 提供了这方面个例子Item 47 提到advance typeid-based(基于 typeid)实现可能会导致编译问题而这就是个产生问题例子:
std::list::iterator iter;
...
advance(iter, 10); // move iter 10 elements forward;
// won\'t compile with above impl.
考虑 advance 为上面这个生成版本用 iter 和 10 类型取代 template parameters(模板参数)IterT 和 DistT的后我们得到这个:
void advance(std::list::iterator& iter, d)
{
(typeid(std::iterator_traits::iterator>::iterator_category)
typeid(std::random_access_iterator_tag)) {
iter d; // error!
}
{
(d >= 0) { while (d--) iter; }
{ while (d) --iter; }
}
}
问题在突出显示行使用了 那行在当前情况下我们试图在个 list::iterator 上使用 但是 list::iterator是个 bidirectional iterator(双向迭代器)(参见C箴言:为类型信息使用特征类)所以它不支持 只有 random
access iterators(随机访问迭代器)才支持 此时我们知道我们永远也不会试图执行那个 行那个 typeid 检测对于list::iterators 永远不成立但是编译器被责成确保所有源代码是正确即使它不被执行而当 iter 不是个
random access iterator(随机访问迭代器)时 \"iter d\" 是不正确traits-based(基于 traits) TMP 解决方案和此对比那里针对区别类型代码被分离到单独中其中每个都只使用了可用于它所针对类型操作
TMP 已经被证明是 Turing-complete(图灵完备)这意味着它强大得足以计算任何东西使用 TMP你可以声明变量执行循环编写和等等但是这些结构看起来和其在“常规”C 中样子非常区别例如C箴言:为类型信息使用特征类展示了 ... 条件在 TMP 中是如何通过 templates(模板)和 template specializations(模板特化)被表达但那是 assembly-level(汇编层次) TMP针对 TMP 库提供了种更高层次语法虽然还不至于让你把它误认为是“常规”C
为了窥其它东西在 TMP 中如何工作让我们来看看 loops(循环)TMP 中没有真正 looping construct(循环结构)因此 loops(循环)效果是通过 recursion(递归)完成(如果你对 recursion(递归)感到不舒服在你斗胆进入TMP 的前定要解决它TMP 很大程度上是个 functional language(性语言)而 recursion(递归)的于 functionallanguage(性语言)就像电视的于美国流行文化:是密不可分)然而甚至 recursion(递归)都不是常规样式 TMPloops 不涉及 recursive function calls(递归)它们涉及 recursive template instantiations(递归模板例子化)
TMP \"hello world\" 在编译期间计算个阶乘它不是个很令人兴奋不过即使不是 \"hello world\"也有助于语言入门TMP 阶乘计算示范了通过 recursive template instantiation(递归模板例子化)实现循环它也示范了在TMP 中创建和使用变量种思路方法看:
template // general : the value of
struct Factorial { // Factorial is n times the value
// of Factorial
enum { value = n * Factorial::value };
};
template // special : the value of
struct Factorial { // Factorial is 1
enum { value = 1 };
};
给出这个 template metaprogram(模板元)(实际上只是单独 template metafunction(模板元
)Factorial)你可以通过引用 Factorial::value 得到 factorial(n) 值
代码循环部分出现在 template instantiation(模板例子化)Factorial 引用 template instantiation(模板例子化)Factorial 地方就像所有正确 recursion(递归)有个导致递归结束特殊情况这里它就是template specialization(模板特化)Factorial
Factorial template 每个 instantiation(例子化)都是个 struct而每个 struct 都使用 enum hack声明了个名为 value TMP 变量value 用于持有阶乘计算当前值如果 TMP 有个真正循环结构value 会在每次循环时更新TMP 在循环位置使用 recursive template instantiation(递归模板例子化)每个 instantiation(例子化)得到它自己 value 拷贝而每个拷贝拥有适合于它在“循环”中所处位置值
你可以像这样使用 Factorial:
{
std::cout ::value; // prs 120
std::cout ::value; // prs 3628800
}
如果你觉得这比吃了冰淇淋还凉快你就具有了个 template metaprogrammer(模板元员)应有素质如果templates(模板)以及 specializations(特化)以及 recursive instantiations(递归例子化)以及 enum hacks 以及对类似 Factorial::value 这样类型需要使你毛骨悚然好吧你是个不错常规 C 员
当然Factorial 示范 TMP 效用大约就像 \"hello world\" 示范任何常规编程语言效用样为了领会为什么TMP 值得了解更好地理解它能做什么是很重要这里是 3个举例:
Ensuring dimensional unit correctness(确保计量单位正确性)在科学和工程应用中计量单位(例如质量距离时间等等)被正确组合是基础例如将个代表质量变量赋值给个代表速度变量是个但是用个时间变量去除距离变量并将结果赋给个速度变量就是正确使用 TMP不论计算多么复杂确保(在编译期间)个中所有计量单位组合都是正确是有可能(这是个如何用 TMP 进行早期诊断例子)这个 TMP 使用个有趣方面是能够支持分数指数这需要这个分数在编译期间被简化以便于编译期能够确认例如单位 time1/2 和单位 time4/8 是相同
Optimizing matrix operations(优化矩阵操作)C箴言:必须返回对象时别返回引用中阐释了些包括
operator*必须返回新 objects而C箴言:从模板中分离出参数无关代码文中则引入了 SquareMatrix 所以考虑如下代码:
typedef SquareMatrix BigMatrix;
BigMatrix m1, m2, m3, m4, m5; // create matrices and
... // give them values
BigMatrix result = m1 * m2 * m3 * m4 * m5; // compute their product
用“常规”思路方法计算 result 需要 4个临时矩阵创建用于每次 operator* 结果此外独立乘法产生了个4次循环遍历矩阵元素序列使用种和 TMP 相关被称为 expression templates(表达式模板)高级模板技术完全不改变上面客户代码语法而消除临时对象以及合并循环是有可能最终软件Software使用更少内存而且运行速度戏剧性地更快
Generating custom design pattern implementations(生成自定义设计模式实现)像 Strategy(参见C箴言:考虑可选虚拟替代思路方法)ObserverVisitor 等设计模式能用很多思路方法实现使用种被称为 policy-baseddesign(基于 policy 设计) TMP-based(基于 TMP)技术使得创建代表独立设计选择 templates (\"policies\")成为可能这种 templates 能以任意思路方法组合以产生带有自定义行为模式实现例如这种技术经常用于允许几个实现了 smart poer behavioral(智能指针行为) policies templates 生成(在编译期间)数百个区别 smartpoer(智能指针)类型将类似设计模式和智能指针这样编程器件范围大大地扩展这项技术是通常所说 generative
programming(产生式编程)基础
TMP 并不适合于每个人它语法是不符合直觉工具支持也很弱(template metaprograms 调试器?哈!)作为个相对晚近才发现“附属”语言TMP programming 规则仍然带有试验性质然而通过将工作从运行时转移到编译时所提供效率提升还是能给人留下深刻印象而表达在运行时很难或不可能实现行为能力也相当有吸引力
TMP 支持程度在不断提升很可能在 C 下个版本中将对它提供直接支持而且 TR1 已经这样做了有关这主题书籍也即将开始出版(目前C Template Metaprogramming: Concepts, Tools, and Techniques from Boostand Beyond 已经出版——译者注)而 web 上 TMP 信息也正在保持增长TMP 也许永远不会成为主流但是对于某些员——特别是库开发者——它几乎必然会成为主料
Things to Remember
·template metaprogramming(模板元编程)能将工作从运行时转移到编译时这样就能够更早察觉并提高运行时性能
2009-2-12 5:14:19
疯狂代码 http://CrazyCoder.cn/