今天在書店裡翻 "C++ template" 學習了一下以前一直不明白的Metaprogramming,發現原來就是那個在模板裡加enum的技巧的應用,仔細想一下果然可以衍生出很多用途,下面是一個最簡單的例子:
#include <iostream>
using namespace std;
template<int n>
class twoPower
{
public:
enum { result = 2*twoPower<n-1>::result };
};
template<>
class twoPower<0>
{
public:
enum {result = 1};
};
int main()
{
cout << twoPower<5>::result <<endl;
}
定義了一個用來算2的n次方的模板,用了模板的特化來做為遞迴的終止條件。第一次接觸到這樣形式的模板應用是在"Modern C++ Design" 的typelist裡,當時就非常的驚訝,發現C++的表達能力實在是我無法想像的。
今天,書店裡思考了一會兒,覺得這個技術可以用來在編譯期實作類別似.net中attribute的東西。回來在機器上試了一下,像這樣:
首先定義一個attribute的模板類,並提供一個預設值:
template<typename T>
class SerializableAttribute
{
public:
enum { IsSerializable = 0 };
};
然後你定義了一個自己的類,比如這樣:
class myClass
{
};
為了應用上面的那個Attribute,你需要為你的類提供一個特化:
template<>
class SerializableAttribute<myClass>
{
public:
enum {IsSerializable = 1 };
};
然後,在其它地方就可能用到這個attribute。(reflect嗎? )
int main()
{
if (SerializableAttribute<myClass>::IsSerializable) cout << "myClass can be serialized." <<endl;
else cout <<"myClass can't be serialized" <<endl;
}
我想以上這種應用,應該就是這種技術被稱為metaprogramming的原因吧。這樣的Attribute和.net中的Attribute最大的大區別是它是靜態,因為模板的推導工作全部都是在編譯期完成的,而不是像.net中那樣將metadata編譯進assembly裡,然後再在執行期運用反射來擷取。這當然會使得它的用途受到諸多限制。
不過有時候我在想,像C++這樣缺乏運行時支援,到底是劣勢呢?還是優勢呢? 最近在研究C#2.0中的泛型機制,其中新提出來的 約束 的機制,我個人猜測很可能就是因為C#太強大的反射機制,導致泛型的推導,不能像C++那樣完全在編譯期完成,才不得不加上的。
C++中的編譯期多態,在C#中似乎也沒有辦法使用了。不過說不定加入泛型的C#,又會生出許多新的應用,誰知道呢! 當初為C++加入模板的時候,stroustup教授自己恐怕也沒有想到泛型會有今天的發展吧。語言總會給我們帶來驚喜,誰說不是呢。