Template Metaprogramming - cont.1铪

MPL 是一个非常聪明的作品,在发挥 C++ 模板威力这方面登峰造极,而其接口的优雅、设计的一致性和思想的完整性让 Loki 这样类似的作品也相形见绌。

废话少说,我们上代码。

#include
#include

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
using namespace boost;

int main()
{
    typedef mpl::list ::type
        type_list;
   
    typedef mpl::find ::type iter;
    cout << typeid(mpl::deref ::type).name() << endl;
   
    typedef mpl::find_if >::type db_iter;
    cout << typeid(mpl::deref ::type).name() << endl;
   
    typedef mpl::find ::type end_iter;
    BOOST_MPL_ASSERT(( is_same ::type> ));
}

在 VC7.1 下面的运行结果是

long
double

上面这个程序表现出很多与 STL 的相似性,首先我们看到了相似的容器:mpl::list (std::list),然后有相似的算法 mpl::find (std::find) ,当然由于 C++ 本身的限制,某些东西需要用模板来模拟,例如 mpl::deref 的作用就相当于运行时的 * (解引用)。我们甚至还看到了 iterator ,在一个 mpl::list 中寻找一个存在的元素,返回的(在概念上)是指向该元素的 iterator ,对这个 iterator 解引用就得到了这个元素本身;如果寻找的元素不存在,返回的是 iterator 就是 mpl::end ::type ,相当于 std::list.end() 。

我们甚至还可以配合 predicate (断言)来使用 mpl::find_if,那么 mpl::_1 是什么?难道是...没错,MPL 甚至提供了构造编译期 lambda 表达式的方法,而用法和 Boost.Lamda 类似。

从这个例子当中我们大概可以一窥 MPL 的雄心勃勃,而实际上学习它也的确要经历一条类似 STL 的学习曲线。每一个负责的 STL 教程都会从 concept 这个东西开始,我们也不如先来看看 MPL 当中的 iterator concept 。

MPL 中有三种 iterator concept: Forward Iterator, Bidirectional Iterator 和 Random Access Iterator 。那么 STL 的 Input Iterator 和 Output Iterator 在哪里?啊哈,如果你去读一读 Dave Abraham, Jeremy Siek 和 Thomas Witt 提交的 New Iterator Concepts 就会知道,原本的 STL concept 系统存在严重的问题,而 MPL 作为一个后来的作品,避免了这些问题,而且 MPL iterator 都是 immutable 的,也就没有什么 Input 和 Output 之分了。

Forward Iterator
Forward Iterator 要满足下面这些 requiremnt:(如果你对 concept, requirement, refinement 这些还不是十分了解,建议你先找一本好的 STL 教材来看看)

Expression Type Complexity
deref::type Any type Amortized constant time
next::type Forward Iterator Amortized constant time
i::category Integral Constant, convertible to forward_iterator_tag Constant time

用 STL 来类比这些东西可能是最快的学习方法。deref ::type 我们刚才看到了,相当于 * ;next::type 也容易理解,相当于 STL 的 ++iter ;而 i::category 呢?它和 STL 的 iterator traits 类似,对于区分 iterator 的类别非常重要。表格中的 Complexity 指的可不是运行时间,而是编译时间复杂度。如果大规模的应用 MPL ,编译时间便会成一个显著的瓶颈,这个时候,了解编译时间的变化趋势对于项目的进行是很重要的。

Bidirectional Iterator
在 STL 中,Bidirectional Iterator 是 models Forward Iterator 的。在这里也一样,除了满足 Forward Iterator 的 requiremnt ,Bidirectional Iterator 还必须满足 prior::type ,这相当于 STL 的 --iter,意义非常明显,在一个序列中后退一步。时间复杂度要求与 next::type 一样。

Random Access Iterator
Random Access Iterator models Bidirectional Iterator,增加了两个 requirement:advance ::type,distance ::type 。前者相当于 iter + n ,也就是在序列中前进 n 步,而且时间复杂度为均摊常数;后者相当于 iter1 - iter2 ,得出的是一个距离值。

我不得不说,MPL 的 iterator 系统比起 STL 来,更加优雅清晰。在 iterator 之后,就可以进入 sequence ,也相当于 STL 容器的介绍了。

Forward Sequence
这是 MPL 当中要求最低的 sequence ,所有的 MPL sequence 都至少要 model Forward Sequence。它的 requirement 包括:

Expression Type Complexity
begin::type Forward Iterator Amortized constant time
end::type Forward Iterator Amortized constant time
size::type Integral Constant Unspecified
empty::type Boolean Integral Constant Constant time
front::type Any type Amortized constant time

至于它们的意义,用相应的 STL 词汇来理解就没有错了。下面的程序把它们全都用到了:

#include
#include

#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
using namespace boost;

int main()
{
    typedef mpl::vector ::type
        type_vect;
   
    typedef mpl::begin ::type begin_iter;
    cout << typeid(mpl::deref ::type).name() << endl;
   
    typedef mpl::find ::type end_iter;
    cout << boolalpha << is_same ::type>::value << endl;
   
    int size = mpl::size ::value;
    cout << size << endl;
   
    bool is_empty = mpl::empty ::value;
    cout << is_empty << endl;
   
    typedef mpl::front ::type first_elem;
    cout << typeid(first_elem).name() << endl;
}

输出:

char
true
6
false
char

很有趣,很直观,比起前面我们做的那些 hack ,看起来容易多了,不是么?想想看如果我们要自己 hand code 干这些事情要花多少精力!

MPL 定义了10种 sequence concept ,族繁不能一一尽述,有了前面的基础,相信 MPL 文档可以成为你最好的伙伴。





你可能感兴趣的:(Template Metaprogramming - cont.1铪)