1 Chapter 2 Metaprogram1.1 Metaprogram The first example
The book presents a computing3OfNPower example, I extend it to computingMOfNPower.CodeAs follows:
Template<Int M,Int N>
Class Power{
Public:
Enum{Result=M*Power<M,N-1> ::Result};
};
Template<Int M>
Class Power<M, 0> {
Public:
Enum{Result= 1 };
};
In fact,Template metaprogramingThe subsequent work is recursive template instantiation.
HerePowerIt is calledTemplate metaprograming. It describes a calculation that can be evaluated during the translation period.
1.2 Enumerated values and static Constants
In the old versionC ++Medium (for exampleVc6), Within the class declaration, the unique method that declares the "real common value" when enumerating values. However, the current situation has changed,C ++In the standardization process, the concept of static constant initialization within the class is introduced (inVs2003).
Therefore, the enumerated values above can be replaced by static constants. The Code is as follows:
Template<Int M,Int N>
Class Power{
Public:
// Enum {result = m * power <m, N-1 >:: result };
Static Int Const Result=M*Power<M,N-1> ::Result;
};
Template<Int M>
Class Power<M, 0> {
Public:
// Enum {result = 1 };
Static Int Const Result= 1;
};
The results are the same.
However, this version has one disadvantage: static member variables can only be left values. Therefore, if you have the following statement:
Void Foo (INT const &);
And youMetaprogramIs:
Foo (power <3, 4 >:: result>;
Then the compiler must passPower <3, 4>: Result. This forces the compiler to instantiate the definition of a static member and allocate memory for the definition.
However, the enumerated value is not the left value (that is, there is no address ). Therefore, when you use reference transfer, no static memory is used, just like passing the calculated value in the form of a text constant. So,Enumeration values are generally encouraged..
1.3 Example 2: Calculate the square root
Template<Int N,Int Lo= 0,Int Hi=N>
Class SQRT{
Public:
Enum{Mid= (Lo+Hi+ 1)/2 };
Enum{Result= (N<Mid*Mid)?SQRT<N,Lo,Mid-1> ::Result:SQRT<N,Mid,Hi> ::Result};
};
Template<Int N,Int M>
Class SQRT<N,M,M> {
Public:
Enum{Result=M};
};
Int _ Tmain(Int Argc,_ Tchar*Argv[])
{
// 17.3
I=SQRT<4> ::Result;
Return0;
}
In the above example, if we expand it, we will get:
SQRT<4> ::Result;
// Mid = (0 + 4 + 1)/2 = 2
= (4 <2*2 )?SQRT<4, 0, 1> ::Result:SQRT<4, 2, 4> ::Result;
//SQRT <4, 0, 1 >:: result
// Mid = (0 + 1 + 1)/2 = 1
= (4 <1*1 )?SQRT<4, 0, 0> ::Result:SQRT<4, 1, 1> ::Result;
= (4 <1*1 )? ;
= 1;
//SQRT <4, 2, 4 >:: result
// Mid = (2 + 4 + 1)/2 = 3
= (4 <3*3 )?SQRT<4, 2, 2> ::Result:SQRT<4, 3, 4> ::Result;
= (4 <9 )? 2:SQRT<4, 3, 4> ::Result;
//SQRT <4, 3, 4 >:: result
// Mid = (3 + 4 + 1)/2 = 4
= (4 <4*4 )?SQRT<4, 3, 3> ::Result:SQRT<4, 4, 4> ::Result;
= (4 <16 )? 3: 4;
= 3;
= 2
= 2
We can see that the following instantiation bodies are obtained:
SQRT<4, 0, 0> ::Result
SQRT<4, 1, 1> ::Result
SQRT<4, 2, 2> ::Result
SQRT<4, 3, 3> ::Result
SQRT<4, 4, 4> ::Result
SQRT<4, 0, 1> ::Result
SQRT<4, 2, 4> ::Result
SQRT<4, 3, 4> ::Result
Since the code tries to use the: operator to enumerate the members of the class, all the members of the class will be instantiated at the same time. This means that not only the template of the positive branch of the conditional operator is instantiated, but also the template of the negative branch is instantiated. This will lead to a large instantiation body.
Template instantiation is usually a costly process for any compiler.