C 箴言:谨慎使用模板元编程

2008-02-23 05:24:10来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 template metaprogramming (TMP)(模板元编程)是写 template-based(基于模板)的运行于编译期间的 C 程式的过程。考虑一下:一个 template metaprogram(模板元程式)是用 C 写的运行于 C 编译器中的程式。当一个 TMP 程式运行完成,他的输出——从 templates(模板)实例化出的 C 源代码片断——随后被正常编译。

  假如您仅把他看作古怪的特性而没有打动您,那您就不会对他有足够的深入的思考。

  C 并不是为 template metaprogramming(模板元编程)设计的,但是自从 TMP 在 1990 年代早期被发现以来,他已被证实很有用,使 TMP 变容易的扩展很可能会被加入到语言和他的标准库之中。是的,TMP 是被发现,而不是被发明。TMP 所基于的特性在 templates(模板)被加入 C 的时候就已被引进了。所需要的全部就是有人注意到他们能够以一种精巧的而且意想不到的方式被使用。

  TMP 有两个强大的力量。首先,他使得用其他方法很难或不可能的一些事情变得容易。第二,因为 template metaprograms(模板元程式)在 C 编译期间执行,他们能将工作从运行时转移到编译时。一个结果就是通常在运行时才能被察觉的错误能够在编译期间被发现。另一个结果是 C 程式使得 TMP 的使用在以下每一个方面都能更有效率:更小的可执行代码,更短的运行时间,更少的内存需求。(然而,将工作从运行时转移到编译时的一个结果就是编译过程变得更长。使用 TMP 的程式可能比他们的 non-TMP 对等物占用长得多的编译时间。)

  考虑STL的advance伪代码。(在《C 箴言:为类型信息使用特征类》中。您现在可能需要读该文,因为在本文中,我假设您已熟悉了该文的内容。),我突出表示代码中的伪代码部分:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (iter is a random access iterator) {
iter = d; // use iterator arithmetic
} // for random access iters
else {
if (d >= 0) { while (d--) iter; } // use iterative calls to
else { while (d ) --iter; } // or -- for other
} // iterator categories
}

  我们能够用 typeid 把伪代码变成真正的代码。这就产生了一个解决此问题的“常规”的 C 方法——他的全部工作都在运行时做:

template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d)
{
if (typeid(typename std::iterator_traits<IterT>::iterator_category) ==
typeid(std::random_access_iterator_tag)) {

iter = d; // use iterator arithmetic
} // for random access iters
else {
if (d >= 0) { while (d--) iter; } // use iterative calls to
else { while (d ) --iter; } // or -- for other
} // iterator categories
}

  《C 箴言:为类型信息使用特征类》中指出这个 typeid-based(基于 typeid)的方法比使用 traits 的方法效率低,因为这个方法,(1)类型检测发生在运行时而不是编译期,(2)用来做运行时类型检测的代码必须出现在可执行代码中。实际上,这个例子展示了 TMP 如何能比一个“常规”C 程式更高效,因为 traits 方法是 TMP。记住,traits 允许编译时在类型上的 if...else 计算。

  我先前谈及一些事情在 TMP 中比在“常规”C 中更简单,而 advance 提供了这方面的一个例子。Item 47 提到 advance 的 typeid-based(基于 typeid)的实现可能会导致编译问题,而这就是个产生问题的例子:

std::list<int>::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<int>::iterator& iter, int d)
{
if (typeid(std::iterator_traits<std::list<int>::iterator>::iterator_category) ==
typeid(std::random_access_iterator_tag)) {

iter = d; // error!
}
else {
if (d >= 0) { while (d--) iter; }
else { while (d ) --iter; }
}
}

  问题在突出显示的行,使用了 = 的那行。在当前情况下,我们试图在一个 list<int>::iterator 上使用 =,但是 list<int>::iterator 是个 bidirectional iterator(双向迭代器)(参见《C 箴言:为类型信息使用特征类》),所以他不支持 =。只有 random access iterators(随机访问迭代器)才支持 =。此时,我们知道我们永远也不会试图执行那个 = 行,因为那个 typeid 检测对于 list<int>::iterators 永远不成立,但是编译器被责成确保任何源代码是正确的,即使他不被执行,而当 iter 不是个 random access iterator(随机访问迭代器)时 "iter = d" 是不正确的。traits-based(基于 traits)的 TMP 解决方案和此对比,那里针对不同类型的代码被分离到单独的函数中,其中每一个都只使用了可用于他所针对的类型的操作。

  TMP 已被证实是 Turing-complete(图灵完备)的,这意味着他强大得足以计算任何东西。使用 TMP,您能够声明变量,执行循环,编写和调用函数,等等。但是这些结构看起来和其在“常规”C 中的样子很不同。例如,《C 箴言:为类型信息使用特征类》展示了 if...else 条件在 TMP 中是如何通过 templates(模板)和 template specializations(模板特化)被表达的。但那是 assembly-level(汇编层次)的 TMP。针对 TMP 的库提供了一种更高层次的语法,虽然还不至于让您把他误认为是“常规”C 。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 菜鸟也能搞定C 内存泄漏

下一篇: 踏入C 中的雷区——C 内存管理详解