摘要:
1。符號查找(對于函數此時只看名字,不看參數) 大致順序是 (1)如果有限定名( XXX:: )那么就直接在XXX里查找 (2)函數局部名字空間 (3)(如果是成員)類名字空間 (4)遞歸向上至所有基類的...
閱讀全文
posted @
2006-12-27 11:04 shifan3 閱讀(2133) |
評論 (8) |
編輯 收藏
發信人: shifan (學習浮云技術), 板面: C++
標 題: 偽typeof
發信站: 飄渺水云間 (Tue Dec 19 16:38:45 2006), 轉信
1 /*
2 用標準C++實現typeof是不可能的
3 這個是我寫的一個approached typeof
4 所有需要被靜態反射出來的類型必須先用DECL_TYPE注冊
5 模板如果僅僅帶有1個參數可以用DECL_TEMPLATE_1注冊
6 多個參數的模板還不支持。。
7 主要是沒想好編碼
8
9 總共能注冊64個類型
10 可以通過MAX_TYPE_NUMBER設置
11
12 支持的模板嵌套層數大約為32 / log2(MAX_TYPE_NUMBER)
13 MAX_TYPE_NUMBER必須為2的整次數冪
14 */
15 namespace my_typeof
16 {
17
18 const int MAX_TYPE_NUMBER = 64;
19
20 template <int N>
21 struct dummy
22 {
23 int a[N];
24 };
25
26
27 template <int N, typename Arg1>
28 struct select_by_number_1;
29
30 template <int N>
31 struct select_by_number
32 {
33 typedef typename select_by_number_1<N % MAX_TYPE_NUMBER, typename
34 select_by_number<N / MAX_TYPE_NUMBER>::type>::type type;
35 };
36
37
38 template <typename T>
39 struct number_of
40 {
41 static const int v = sizeof(generic_f(*(T*)0)) / sizeof(int);
42 };
43
44
45 #define DECL_TYPE(T, N) \
46 namespace my_typeof{ \
47 template<>\
48 struct select_by_number<N> \
49 {\
50 typedef T type;\
51 };\
52 dummy <N> generic_f(const T&);}
53
54
55 #define DECL_TEMPLATE_1(T, N) \
56 namespace my_typeof{ \
57 template<typename Arg1>\
58 struct select_by_number_1<N, Arg1>\
59 {\
60 typedef T<Arg1> type;\
61 };\
62 template <typename Arg1>\
63 dummy<N + number_of<Arg1>::v * MAX_TYPE_NUMBER > generic_f(const T<Arg1>&);}
64
65
66
67 #define TYPE_OF(x) my_typeof::select_by_number<sizeof(my_typeof::generic_f(x)) /
68 sizeof (int)>::type
69
70 }
71
72
73 //sample
74 #include <iostream>
75 #include <vector>
76 #include <list>
77
78
79 DECL_TYPE(int, 1);
80 DECL_TEMPLATE_1(std::vector, 2);
81 DECL_TEMPLATE_1(std::list, 3);
82 DECL_TYPE(double, 4)
83
84 using namespace std;
85 int main(int, char*[])
86 {
87 vector<list<vector<list<double> > > > v1;
88 TYPE_OF(v1) v2;
89 v1 = v2;
90 return 0;
91 }
92
93
--
You well 撒法!You well all 撒法!
※ 內容修改:·shifan 于 Dec 21 14:21:57 修改本文內容·[FROM: shifan]
※ 來源:·飄渺水云間 freecity.cn·[FROM: shifan]
posted @
2006-12-21 14:29 shifan3 閱讀(2655) |
評論 (8) |
編輯 收藏
真令人傷感,每過幾年就是離別時。
我站在窗前,無奈的看著朋友們為了工作奔走,
而不能幫上哪怕一點忙。
就像十年前,就像六年前,就像三年前
就像每一次與人擦肩而過卻故意目不斜視。
但這一次也許好一點,畢竟我們以后可能還能時常相聚,
而不像現在只能看著從前的好友的博客獨自傷感。
那種陌生,令人心碎。
?
posted @
2006-11-01 00:31 shifan3 閱讀(600) |
評論 (4) |
編輯 收藏
boost的integer/integer_mask.hpp僅僅做了單個位的bit mask
要多個位必須寫很多遍high_bit_mask_t
使用low_bits_mask_t也不能完全解決問題
所以自己用Typelist的那種寫法寫了一個
用法舉例
bit_mask<INT_LIST_2(2, 3)>::value返回一個值,該值的第2、3位被置為1
其余位為0
1
2 namespace multi_bit_mask
3 {
4 namespace details
5 {
6
7 template <typename T>
8 struct get_size
9 {
10 enum {size = sizeof(T)};
11 };
12
13 template <int Bit>
14 struct bit_storage
15 {
16 typedef typename bit_storage<Bit - 1>::storage_type storage_type;
17 };
18
19 //---------platform dependency-----------------------
20
21 typedef unsigned int smallest_storage_type;
22 typedef unsigned long long largest_storage_type;
23
24
25
26 template <>
27 struct bit_storage<0>
28 {
29 typedef smallest_storage_type storage_type;
30 };
31
32 template <>
33 struct bit_storage<get_size<smallest_storage_type>::size * 8>
34 {
35 typedef largest_storage_type storage_type;
36 };
37
38 //disable the 65th bit
39 template <>
40 struct bit_storage<get_size<largest_storage_type>::size * 8>
41 {
42 typedef void storage_type;
43 };
44
45 //---------end of platform dependency----------------
46
47
48 template <unsigned int N, typename Next>
49 struct int_list
50 {
51 typedef typename bit_storage<N>::storage_type storage_type;
52 static const storage_type value = N;
53 typedef Next next;
54 };
55
56 struct null_type{};
57
58 template<typename T1, typename T2, bool is_first>
59 struct selector
60 {
61 typedef T1 type;
62 };
63
64 template<typename T1, typename T2>
65 struct compare_type
66 {
67 const static bool is_larger = sizeof(T1) > sizeof(T2);
68 typedef typename selector<T1, T2, is_larger>::type large_type;
69 typedef typename selector<T1, T2, !is_larger>::type small_type;
70 };
71
72
73
74 template<typename T1, typename T2>
75 struct selector<T1, T2, false>
76 {
77 typedef T2 type;
78 };
79
80 template <typename List>
81 class find_largest_storage
82 {
83 typedef typename find_largest_storage<typename List::next>::storage_type T1;
84 typedef typename bit_storage<List::value>::storage_type T2;
85 public:
86 typedef typename compare_type<T1, T2>::large_type storage_type;
87 };
88
89 template <>
90 class find_largest_storage<null_type>
91 {
92 public:
93 typedef smallest_storage_type storage_type;
94 };
95
96
97 }
98
99
100
101
102
103 template <int N>
104 struct single_bit_mask
105 {
106 typedef typename details::bit_storage<N>::storage_type storage_type;
107 static const storage_type value
108 = static_cast<storage_type>(single_bit_mask<N - 1>::value) * 2;
109 };
110
111 template <>
112 struct single_bit_mask<0>
113 {
114 typedef details::bit_storage<0>::storage_type storage_type;
115 static const storage_type value = 1;
116 };
117
118
119 typedef details::null_type null_type;
120
121 template <int N, typename Next>
122 struct int_list_t : public details::int_list<N, Next> {};
123
124 template <typename List>
125 struct bit_mask
126 {
127 public:
128
129 typedef typename details::find_largest_storage<List>::storage_type storage_type;
130
131 static const storage_type value
132 = static_cast<storage_type>(single_bit_mask<List::value>::value)
133 | static_cast<storage_type>(bit_mask<typename List::next>::value);
134 };
135
136 template <>
137 struct bit_mask<null_type>
138 {
139 typedef details::bit_storage<0>::storage_type storage_type;
140 static const storage_type value = 0;
141 };
142
143
144
145
146
147 #define INT_LIST_1(n1) multi_bit_mask::int_list_t<n1, multi_bit_mask::null_type>
148 #define INT_LIST_2(n1, n2) multi_bit_mask::int_list_t<n1, INT_LIST_1(n2) >
149 #define INT_LIST_3(n1, n2, n3) multi_bit_mask::int_list_t<n1, INT_LIST_2(n2, n3) >
150 #define INT_LIST_4(n1, n2, n3, n4) multi_bit_mask::int_list_t<n1, INT_LIST_3(n2, n3, n4) >
151 #define INT_LIST_5(n1, n2, n3, n4, n5) multi_bit_mask::int_list_t<n1, INT_LIST_4(n2, n3, n4, n5) >
152 #define INT_LIST_6(n1, n2, n3, n4, n5, n6) multi_bit_mask::int_list_t<n1, INT_LIST_5(n2, n3, n4, n5, n6) >
153 #define INT_LIST_7(n1, n2, n3, n4, n5, n6, n7) multi_bit_mask::int_list_t<n1, INT_LIST_6(n2, n3, n4, n5, n6, n7) >
154 #define INT_LIST_8(n1, n2, n3, n4, n5, n6, n7, n8) multi_bit_mask::int_list_t<n1, INT_LIST_7(n2, n3, n4, n5, n6, n7, n8) >
155
156 }
157
158
159
sample
#include < iostream >
#include " multi_bit_mask.h "
using namespace std;
int main()

{
cout << multi_bit_mask::bit_mask < INT_LIST_1( 1 ) > ::value << endl;
cout << multi_bit_mask::bit_mask < INT_LIST_5( 0 , 1 , 2 , 3 , 4 ) > ::value << endl;
cout << multi_bit_mask::bit_mask < INT_LIST_7( 0 , 1 , 2 , 3 , 4 , 4 , 2 ) > ::value << endl;
posted @
2006-10-26 23:37 shifan3 閱讀(1460) |
評論 (2) |
編輯 收藏
近日在學校bbs上與人討論C++的typeid關鍵字的實現問題,有人提到type_info的地址是存放在虛表的第一個位置上,頗覺得不妥,于是我在vc2003下實驗了一番
在vc下,使用typeid的時候,如果typeid施加給的類型是沒有vptr的class或者根本不是class
那么匯編是
mov dword ptr [addr],offset A `RTTI Type Descriptor' (42AD40h)
也就是編譯器生成一個簡單的type_info對象的表,并且在編譯期靜態決定下標,做一個簡單查表操作。
如果typeid的操作對象是具有vptr的class,但是并不是一個引用或者指針的解引用形式,例如
A a;
typeid(a);
那么仍然僅僅會做查表操作
如果typeid的操作對象是具有vptr的class,并且是引用或者指針的解引用形式,例如
A * p = new A;
A & r = * p;
typeid( * p);
typeid(r);
那么就會調用一個叫___RTtypeid的函數,并通過某種方法來獲取type_info對象
下面是___RTtypeid的反匯編,這里只列出關鍵的幾條指令
0041213E mov ecx,dword ptr [inptr] ;inptr是對象的地址
00412141 mov edx,dword ptr [ecx]
00412143 mov eax,dword ptr [edx - 4 ]
0041215F mov ecx,dword ptr [eax + 0Ch]
00412162 mov dword ptr [ebp - 48h],ecx
0041216C mov eax,dword ptr [ebp - 48h]
基本上等價于C語言的
int a1 = ( int )p; // p是對象的地址
int a2 = * ( int * )a1 - 4 ;
int a3 = * ( int * )a2 + 12 ;
int a4 = * ( int * )a3;
那么從這段代碼可以看出vc下type_info對象的存放位置[如下圖]
也就虛表下標為-1的位置上存放了一個指向一個未知的表的指針(暫且將此表命名為runtime_info_table)
runtime_info_table的第4格上存放了type_info對象的地址
至于runtime_info_table里前3格上存放的是什么, 還需要再研究研究
一般來說它們全是0, 但是對于多重虛繼承的類, 第二格上會是4, 可能和指針的偏移量有關.
posted @
2006-10-26 10:46 shifan3 閱讀(3357) |
評論 (5) |
編輯 收藏
Xpressive是一個C++的正則表達式庫,目前是Boost的候選庫。
Xpressive和Boost.Regex的區別很大。首先,Xpressive是一個純頭文件的庫,也是說,在使用之前不需要預先編譯。其次,Xpressive支持類似于Spirit的靜態語義定義。
我們先來看一個例子:
#include <iostream>
#include <boost/xpressive/xpressive.hpp>
using namespace boost::xpressive;
int main()
{
std::string hello( "hello world!" );
sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
smatch what;
if( regex_match( hello, what, rex ) )
{
std::cout << what[0] << '\n'; // whole match
std::cout << what[1] << '\n'; // first capture
std::cout << what[2] << '\n'; // second capture
}
return 0;
}
這是使用Xpressive動態語義定義的例子,其中sregex::compile函數編譯一個表示正則文法的串,并返回一個正則對象sregex
使用regex_match來使用這個正則對象匹配一個串。結果儲存在what內
其中what[0]返回整個串,what[1]~what[n]返回文法中用于標記的部分(用小括號括起來的部分)
最后將輸出
hello world!
hello
world
如果想在一個串中查找符合該文法的子串,可以使用regex_search,用法和regex_match一樣,此外還可以用regex_replace來進行替換。
靜態文法:
Xpressive除了可以用compile來分析一個文法串之外,還可以用類似于Spirit的方式來靜態的指定文法:
sregex re = '$' >> +_d >> '.' >> _d >> _d;
這將定義一個表示金額的串,其中_d表示一個數字,相當于串 $\d+.\d\d
這樣定義文法將比之前的動態定義更加高效,并且還有一個附加的好處:
分級定義:
sregex re = '$' >> +_d >> '.' >> _d >> _d;
sregex s = '(' >> re >> ')';
這樣s表示為用括號括起來的re
通過分級定義,文法能被表示的更加清楚。
更加棒的是,分級定義還可以向后引用,因此能夠分析EBNF
sregex group, factor, term, expression;
group = '(' >> by_ref(expression) >> ')';
factor = +_d | group;
term = factor >> *(('*' >> factor) | ('/' >> factor));
expression = term >> *(('+' >> term) | ('-' >> term));
expression定義了一個四則表達式,注意其中group的定義。
這里必須使用by_ref是因為Xpressive默認是值拷貝,如果這里使用默認的方式,那么會造成一個無限循環。
Xpressive可以在這里下載
http://boost-consulting.com/vault/index.php?PHPSESSID=f1d4af8b742cfa7adae7aab373cfc535&direction=0&order=&directory=Strings%20-%20Text%20Processing&PHPSESSID=f1d4af8b742cfa7adae7aab373cfc535
內有詳細的文檔
posted @
2006-07-27 16:27 shifan3 閱讀(3123) |
評論 (4) |
編輯 收藏
看了546@C++@Freecity之后,發覺非常有意思,由此產生一些想法
很多時候寫一個類的時候,需要多個模版參數,例如一個遺傳算法的算法類,需要一個模版參數來指定交配方式,另一個模版參數來指定子代選擇的方式,還要一個參數來指定變異的方式。那么一般來說,這個類會寫成:
template<class T //描述問題的一個類
, class CrossPolicy = AvgCrossPolicy //雜交方式
, class SelectPolicy = DefaultSelectPolicy //子代選擇的方式
, class VariationPolicy = ReverseVariationPolicy> //變異方式
class Gene
: private AvgCrossPolicy
, private SelectPolicy
, private VariationPolicy
{
....
};
這樣用戶要使用該類的時候,可以直接指定T,就行了,然而如果要指定變異方式,那么就必須把所有的參數都顯式的寫出來,很不方便
546提供了一種有效的方法,可以讓我們僅僅指定變異參數,而不用寫出另兩個Policy
甚至允許我們以任意的順序書寫幾個Policy參數,都不會有問題
預備知識:
TypeList
一個TypeList是一個類型的容器
template <typename Type_, typename Next_>
struct TypeList
{
typedef Type_ Type;
typedef Next_ Next;
};
這就是一個TypeList。
看這個寫法,是不是像一個鏈表?
首先定義一個類型來表示鏈表尾:class NullType{};
現在一個包含了2個類型的TypeList就可以寫為:
TypeList<T1, TypeList<T2, NullType> >
如何在一個TypeList中查找一個類型的子類?
首先要有一個IsDerivedFrom<Base, T>
這個比較簡單
template<class Base, class T>
class IsDerivedFrom
{
struct large{char a[2];};
static char pred(Base*);
static large pred(...);
public:
enum {Is = sizeof(pred((T*)0)) == sizeof(char)};
};
然后FindChild就容易了
template <class List, class Base>
struct FindChild
{
template <bool IsChild>
struct Select
{
typedef typename List::Type Type;
};
template <>
struct Select<false>
{
typedef typename FindChild<typename List::Next, Base>::Type Type;
};
typedef typename Select<IsDerivedFrom<Base, typename List::Type> >::Type Type;
};
當然還要對一些特殊情況進行特化,例如NullType
template <class Base>
struct FindChild<NullType, Base>
{
typedef NullType Type;
};
這里使用NullType來表明沒找到
實際操作:
首先需要給3個Policy3個基類,分別叫
class AvgCrossPolicyBase{};
class SelectPolicyBase{};
class VariationPolicyBase{};
內容為空就行了,這樣也沒有虛函數調用的開銷
然后聲明一個類來表示默認情況:
class DefaultPolicy{};
定義一個宏
#define TYPELIST_3_N(a, b, c) TypeList<a, TypeList<b, TypeList<c, NullType> > >
下面要寫一些選擇器,用于把合適的類型選擇出來,如果沒找到,則要使用默認的類型
template <class List, class Base, class DefaultType>
struct Selector
{
template <class RetType>
struct Judge
{
typedef RetType Type;
};
template<>
struct Judge<NullType>
{
typedef DefaultType Type;
};
typedef typename Judge<typename FindChild<List, Base>::Type >::Type Type;
};
好啦,現在整個類的聲明可以寫為
template<class T
, class CrossPolicy_ = DefaultPolicy
, class SelectPolicy_ = DefaultPolicy
, class VariationPolicy_ = DefaultPolicy //其后的參數用戶不可指定
, class List = TYPELIST_3_N(CrossPolicy_, SelectPolicy_, VariationPolicy_)
, class CrossPolicy = typename Selector<List, CrossPolicyBase, AvgCrossPolicy>::Type
, class SelectPolicy = typename Selector<List, SelectPolicyBase, DefaultSelectPolicy>::Type
, class VariationPolicy = typename Selector<List, VariationPolicyBase, ReverseVariationPolicy>::Type
>
class Gene
: private CrossPolicy
, private SelectPolicy
, private VariationPolicy
{
....
};
其中第4-7個參數(List,CrossPolicy,SelectPolicy和VariationPolicy)是不由用戶指定的,僅僅是為了起一個別名
第一個參數T必須指定,然后2,3,4這3個參數就可以任意的改變順序了
例如,可以寫Gene<T, DefaultSelectPolicy, AvgCrossPolicy>而不會有任何問題
如果不想要最后面幾個參數的話也行,但是代碼就要稍微長一點
而且最好在類里面進行3個typedef
typedef typename Selector<List, CrossPolicyBase, AvgCrossPolicy>::Type CrossPolicy;
等,以便在實現的時候使用
posted @
2006-07-24 01:06 shifan3 閱讀(1019) |
評論 (9) |
編輯 收藏
摘要: 發信人: shifan (家沒有豚豚 T.T), 板面: C++標 題: 如何實現Lambda[第二部分]發信站: 飄渺水云間 (Thu Jun 8 23:30:20 2006), 轉信
章節:八:第一部分的小結九:簡化,如何減少Lambda代碼的冗余和依賴性十:bind的實現十一:實現phoenix
八. 中期總結目前的結果是這樣的...
閱讀全文
posted @
2006-07-15 15:32 shifan3 閱讀(1005) |
評論 (0) |
編輯 收藏
摘要: 一. 什么是Lambda所謂Lambda,簡單的說就是快速的小函數生成。在C++中,STL的很多算法都要求使用者提供一個函數對象。例如for_each函數,會要求用戶提供一個表明“行為”的函數對象。以vector<bool>為例,如果想使用for_each對其中的各元素全部賦值為true,一般需要這么一個函數對象,
c...
閱讀全文
posted @
2006-06-09 13:23 shifan3 閱讀(3003) |
評論 (7) |
編輯 收藏
最近為了解析SQL語法,懷著試一試的心態去翻了翻boost的spirit庫,因為該庫的文檔的簡介里寫著LL parser framework represents parsers directly as EBNF grammars in inlined C++。看著framework這個詞自然覺得這個庫很牛B,試用了一下果然如此。
所謂EBNF即擴展巴克斯范式,是一種描述Context-Free Language的文法。在目前常見的非自然語言中,大部分都可以用EBNF表示。例如:
group ::='('exp ')'
factor ::=integer| group
term ::=factor(('*'factor)|('/'factor ))*
exp ::=term(('+'term)|('-'term ))*
這是一個整數表達式的EBNF。該段描述用spirit在C++中的實現則是:
rule<> group, factor, term, exp;
group = '(' >> exp >> ')';
factor = int_p | group;
term = factor >> *(('*' >> factor) | ('/' >> factor));
exp = term >> *(('+' >> term) | ('-' >> term));
這里使用=代替::=, 用>>代替空格連接。并且由于C++語法所限,EBNF中后置的*在spirit中改為前置。
等式左邊的單詞被稱為一個rule,等式右邊為rule的定義。我們可以看出一個group是一個exp加上一對括號,一個factor是一個整數或者一個group,一個term是一個或多個factor用*/連接,一個exp是一個或多個term用+-連接。處于最頂端的exp可以據此識別出以下表達式
12345
-12345
+12345
1 + 2
1 * 2
1/2 + 3/4
1 + 2 + 3 + 4
1 * 2 * 3 * 4
(1 + 2) * (3 + 4)
(-1 + 2) * (3 + -4)
1 + ((6 * 200) - 20) / 6
(1 + (2 + (3 + (4 + 5))))
得到一個rule之后,我們就可以用 parse函數對一個串進行識別了。例如
parse( " (1 + (2 + (3 + (4 + 5)))) " , exp);
該函數返回一個結構parse_info,可以通過訪問其中的full成員來判斷是否成功識別,也可以訪問stop成員來獲知失敗的位置。這里要特別提一點,關于各個符號之間的空格,spirit的文檔的正文說的是給parse再傳一個參數space_p,通知parse跳過所有的空格,然而在FAQ中又提到,如果使用以上方法定義rule,第三個參數傳space_p會失敗。原因是使用rule默認定義的規則被稱為character level parsing,即字符級別解析,而parse的第3個參數僅適用于phrase level parsing,即語法級別解析。要使用第3個參數可以有幾種方法。
1。在parse的第二個參數直接傳入一個EBNF表達式,不創建rule對象。
parse( " hello world " , * anychar_p, space_p);
2。以rule<phrase_scanner_t>創建rule。
rule < phrase_scanner_t > exp;
注意雖然可以用這兩個辦法屏蔽空格,但是這樣可能完全改變EBNF文法的語義,尤其是在語言本身需要識別空格的時候。對于這種情況,可以不使用第三個參數,并在需要出現空格的地方加上space_p,或者+space_p及*space_p,其中+和*分別表示后面的符號連續出現一次以上和0次以上。例如一個以空格分隔的整數列表可以寫成int_p >> *(+space_p >> int_p)
如上使用parse可以識別一個串,但并不能做更多的操作,例如將語法里的各個成分提取出來。對于這樣的需求,可以通過actor實現。下面是使用actor的一個簡單例子
bool
parse_numbers(char const* str, vector<double>& v)

{
return parse(str,

// Begin grammar
(
real_p[push_back_a(v)] >> *(',' >> real_p[push_back_a(v)])
)
,
// End grammar
space_p).full;
}
注意到real_p后面的[],中括號里面是一個仿函數(函數指針或者函數對象),該仿函數具有如下調用型別
void operator()(IterT first, IterT last) const;
void operator()(NumT val) const;
void operator()(CharT ch) const;
一旦spase發現了匹配real_p的子串,就會調用該functor。不同的rule可能會對應不同的調用型別。
第一個型別針對一般規則,first和last為兩個指向字符的迭代器(一般為char*),匹配的子串為[first, last)
第二個型別針對數字型規則,如real_p和int_p, 參數val是一個數字類型。
第三個性別針對單字符型規則,如space_p, 參數ch是一個字符類型。
real_p[push_back_a(v)]中的push_back_a是一個spirit已經定義好的functor,它會將匹配好的內容依照匹配到的時間順序調用v的push_back函數加入到v中。
到此spirit的常用功能就都介紹完了。要詳細深入了解可以參考spirit的文檔。
最后在題一個注意要點。spirit的各種EBNF連接都是指針連接,因此才能在expression被賦值前就在group的定義里面使用。所以在使用EBNF的時候一定要小心不要將局部變量的rule提供給全局或者類成員變量使用,例如:
class A

{
rule<> s;
A()

{
rule<> r = int_p | hex_p;

s = r >> *(+space_p >> r); //error, r destructed after return
}
};

如果真想使用局部作用域,可以在局部的rule前面加上static.
posted @
2005-12-18 12:02 shifan3 閱讀(7130) |
評論 (5) |
編輯 收藏