[Collection] C ++ template element programming [Metaprogram]

xiaoxiao2021-03-06  118

C template programming [metaprogram] by Micolai Josuttis, David Vandevoorde is taken from C Templates: The Complete Guide (Translate This article, all introducing a new programming method . All rights reserved For the original, the author did not dare to borrow it. The original text has many mistakes, and the families cannot count. The author's level is limited, and it will be changed to the original, and it is not changed to the reader. Some of the obvious mistakes in the text have been corrected according to their own understanding, and the original text is listed on the side, in order to check the test. And the program code in the article is not allowed to check, if there is a mistake, I hope to understand. Original typhistical, the author tries to figure out the translation, dinosing, more in the first appearance, in the first appearance, in the brackets, such as: metaprogram [metaprogramming]; translation or line replenishment, all follow this example. The text in the square brackets is also the author of the pen; the cracker is from the original. This article is taken from "C Templates: The Complete Guide" Chapter 17, and the number of questions and pages refer to the original book. Interested readers can check the contents of the original book. ] 17.1 Metac Programming [Metaprogram]

"Metal Programming" refers to "the 'program of the edible program". In other words, we only give the layout of the code, and the programming system generates code when runtime to implement the features we want. Typically, the word "meta-programming" means a "add-to-itself" characteristic - meta-programming components will eventually become part of its product code / program.

What is the attractive thing? Like other major programming methods, meta-programming goals are also to exchange as fewer efforts to exchange as much as possible - the "effort" here can also be measured using code length, maintenance costs, or other standards. The uniqueness of meta-programming is that some user-defined calculations can occur in the translation period. [Metal programming] Potential motivation either for efficiency (something that can be calculated usually optimized), or to simplify the interface (metaprogram] generally smaller than the final program after the expansion), or two Dependent. Yuan programming often depends on the concept of Traits and Type functions developed in Chapter 15. Therefore, we suggest you, before studying this chapter, you should have the previous chapter in the chest.

1 yuan programming first example

During the 1994 C Standardization Committee meeting, Erwin Unruh found that you can perform some calculations in the compile period with template [Templates]. He [with this method] written a program that generates a lot of money. The most disturbed part of this small exercise is that the result of the rigidity is done by the compiler during the compilation process instead of running a period. In particular, the compiler also generates a series of error messages [Error Message] for each of the numbers between 2 to a particular value [Error Message]. Although the transplantability of this program is not particularly strong (because the error message is not standardized), this program does demonstrate the modification [Template Instantiation] mechanism can be used as an primary recursive language to implement some in the compile period. More complex calculation work ability. This technique of presenting calculations through template is usually referred to as "template meta programming [Template Metaprogramming].

For a glimpse of the glue, we started from a simple exercise (Erwin's programs will be displayed to everyone on page 318). The following program shows how to calculate 3 of the compile period:

// Meta / Pow3.hpp # ifNDef Pow3_HPP # Define Pow3_HPP

// primary template to compute 3 to the n; template class power {public: enum {result = 3 * POW3 :: result};

// Full Specialization to End the recuty} template <> class power <0> {public: enum {result = 1};

#ENDIF / / POW3_HPP

The driving force behind the template programming is a modest [Recursive Template Instantiation] of the template [Recursive Template Instantiation]. In our program, in order to calculate 3 ^ n, we use the following two rules to drive the template's recording equipment:

1. 3 ^ n = 3 * 3 ^ (N-1) 2. 3 ^ 0 = 1

The first template implements usual recursive rules:

Template Class Pow3 {public: enum {result = 3 * POW3 :: results};

When using a positive integer N to develop this template, the template POW <3> must first calculate the enumeration value Result it. This enumeration value is also defined as the corresponding value after the same template is displayed by N-1.

The second template is a special version, which gives the end point of recursive. It is just a result value when POW3 <0>:

Template <> Class Pow3 <0> {public: enum {result = 1};

If you use this template to calculate 3 ^ 7, just a POW3 <7> can be used. Now let's study it, when we have this template, what happened to:

#include #include "pow3b.hpp"

Int main () {std :: cout << "Pow3 <7> :: result =" << pow3 <7> :: result << '/ n';}

First of all, the compiler is now a new POW3 <7>, its result value is: 3 * Pow3 <6> :: result [% of this original POW3 <5> is incorrect, change] then need to use 6 template. According to such push, POW3 <6> will have a POW3 <5>, and the latter is also developed by POW3 <4> ... When it is activated to POW3 <0>, the value of Result is set to 1, Recursive end.

Pow3 <> This template (including its specialization) is called "Template Program [Template Metaprotram]. This program describes some operations, which will be performed in the translation period with the modification of the template. This example is relatively simple, but also can't see how much help to us, but it has been this point, [Miller] This tool is already a hand.

17.2 Enumeration Value [Enumeration VALUES] VS Static Constant [static constants]

In the old C compiler, you want to use "true constant" in the class declaration (so-called "constant expression" [constant-expression]), the enumeration value is the only choice. However, since C standardization, the situation has changed. The C standard proposes the concept of so-called "class static constant initialization" [In-Class Static Constant Initializer]. The following simple example introduces the structure of this mechanism: struct trueconstants {enum {three = 3}; static int covest four = 4;

In this example, Four is also a "real constant" - as so.

With this mechanism, our POW3 program can be implemented as follows:

// Meta / Pow3b.hpp

#ifndef power3_HPP # define Pow3_HPP

// Primary Template to Compute 3 to the nth

Template class power {public: static int covest result = 3 * POW3 :: results;}

// Full Specialization to End The RecursionTemplate <> Class Pow3 <0> {public: static int const result = 1;

#ENDIF / / POW3_HPP

The only difference between this version and the previous version is the use of a static constant member of the class instead of the enumeration value in the previous version. However, this version has a disadvantage: static constant members are left value [LVALUE]. So if you declare such a function:

Void foo (int covest);

And pass the results of the programs to this function:

Foo (pow3 <7> :: result);

The compiler must transmit the address of the POW3 <7> :: Result to the function, which will force the compiler to generate entities and allocate space for static members. In this way, the impact of this calculation work is no longer limited to "compile period".

Enumeration value is not left value (meaning it does not have actual address). Therefore, even if you call it in the "inlet [By Reference]", you will not use any static memory. The overhead of passing enumeration values ​​is almost entirely equivalent to transmitting the results as a constant symbol [literal]. Based on the above reasons, we all use enumeration values ​​in all metrorats of this book.

17.3 Another example: calculate the square root

Let us look at a slightly complicated example: How to write a programs to calculate the square root of the given value N. This element program should be similar to such (where the technique used herein will be explained later):

// meta / sqrt1.hpp

#ifndef SQRT_HPP # Define SQRT_HPP

// primary template to compute sqrt (n) template Class SQRT {public: // compute the midpoint, rouded up enum {mid = (LO HI 1) / 2 };

// Search a NOT TOO LARGE VALUE IN A Halved Interval Enum {result = (n :: Result: SQRT :: result} ;}; // Partial Specialization for the Case When Lo Equals HITEMPLATE Class SQRT {public: enum {result = m};

#ENDIF // SQRT_HPP

The first template is a common recursive calculation, which has a template parameter N (the value of the square root) and the other two optional parameters. The latter template represents the range of value of the returned square root value. If you use only one template parameter to call this template, as we know, the square root is minimum is 1, and the maximum will not exceed the number of revolutions itself.

Our recursive benefits from two-point lookup technology [Binary Search Technique] (in this context, it is often referred to as "two-point method [Method of Bisection]). In the template inside, we first calculate the resulting result is the first half of the area of ​​LO to Hi or the second half, this branch selection is achieved by: operator implementation. If the MID ^ 2 is larger, we will continue to look for the first half; if the MID ^ 2 is less than or equal to N, we will use the same template to find the second half.

When LO and HI are equal to the same value M, the Chart Template will terminate this recursive, and this m is the result we seek.

Let's take another example of using this metrorator:

// meta / sqrt1.cpp

#include #include "sqrt1.hpp"

Int main () {std :: cout << "SQRT <16> :: result =" << SQRT <16> :: result << '/ n'; std :: cout << "SQRT <25> :: Result = "<< SQRT <25> :: result << '/ n'; std :: cout <<" SQRT <42> :: result = << SQRT <42> :: result << '/ n' ; Std :: cout << "SQRT <1> :: result =" << SQRT <1> :: result << '/ n';}

expression

SQRT <16> :: result

Will be expanded

SQRT <16, 1, 16> :: result

In the template inside, the programs will calculate the value of SQRT <16, 1, 16> :: result in the following:

MID = (1 16 1) / 2 = 9Result = (16 <9 * 9)? SQRT <16, 1, 8> :: result: SQRT <16, 9, 16> :: result = (16 <81 )? SQRT <16, 1, 8> :: result: sqrt <16, 9, 16> :: result = SQRT <16, 1, 8> :: result calculation results are: Result equal to SQRT <16, 1, 8 > :: result, while the latter will be deployed: MID = (1 8 1) / 2 = 5Result = (16 <5 * 5)? SQRT <16, 1, 4> :: Result: SQRT <16, 5, 8> :: result = (16 <25)? SQRT <16, 1, 4> :: result: sqrt <16, 5, 8> :: result = sqrt <16, 1, 4> :: result

Similarly, SQRT <16, 1, 4> :: result will be decomposed as:

MID = (1 4 1) / 2 = 3Result = (16 <3 * 3)? SQRT <16, 1, 2> :: Result: SQRT <16, 3, 4> :: Result = (16 <9 )? SQRT <16, 1, 2> :: result: sqrt <16, 3, 4> :: result = SQRT <16, 3, 4> :: result

Finally, the calculation results of SQRT <16, 3, 4> :: Result are:

MID = (16 4 1) / 2 = 4RESULT = (16 <4 * 4)? SQRT <16, 3, 3> :: Result: SQRT <16, 4, 4> :: result = (16 <16 )? SQRT <16, 3, 3> :: result: SQRT <16, 4, 4> :: result = SQRT <16, 4, 4> :: result

SQRT <16, 4, 4> :: Result will terminate this recursive because this template (upper and lower and lower is equal) matches the explicit version of the version. The final result will be:

Result = 4 [Translation: The original is result = 5, suspected is a pen error]

Dialysis is full of activities [all instantiations] ================================

In the above example, in fact, we have a quite a number of template examples, such as in the first iteration of the calculation square root:

(16 <= 8 * 8)? SQRT <16, 1, 8> :: result: SQRT <16, 9, 16> :: result this branch not only has a positive branch of conditional statements [Press: Refers to SQRT <16, 1, 8>], and also has a negative branch (SQRT <16, 9, 16>). More more, because the code code is trying to pass :: operator access to a member of the type [Class Type], all of this type of member must be turned out. This means that the full activities of SQRT <16, 9, 16> will include SQRT <16, 9, 12> and SQRT <16, 13, 16> each of which are fully agreed. If you carefully explore the details of this whole process, we will find that there are dozens of template instances have been turned out. The number of all template instances is almost twice the N value. This is very unpleasant, because of most compilers, the template is a quite expensive operation, especially for memory consumption. Fortunately, there is a technique to reduce the sharp expansion of this template instance. We can choose the required computing structure by specialization [Specialization], not use?: Operators to choose. In order to demonstrate this technology, let us rewrite the secondary program of SQRT:

// meta / sqrt2.hpp

#include "ifthenelse.hpp"

// primary template for main recursive stepTemplate class sqrt {public: // compute the midpoint, rouded up enum {mid = (LO HI 1) / 2};

// Search a NOT TOO LARGE VALUE IN A Halved Interval Typef TypeName ifthenelse <(n , SQRT > :: resultt subt; enum {Result = Subt :: result};

// Partial Specialization for End of Recursion CriterionTemplate Class SQRT {public: enum {result = s};

The key here is the use of the IFThenelse template. See Section 15.2.4 on page 272 for this template:

// meta / ifthenenelse.hpp

#ifndef ifthenenelse_hpp # define ifthenelse_HPP

// Primary Template: Yield Second Or Third Argument Depending On First Argument DEPETORT ARGUMENT

Template Class ifthenelse;

// Partial Specialization: True Yields Second ArgumentTemplate Class iftheneLse {

PUBLIC: TYPEDEF TA RESULTT;

// Partial Specialization: Flase Yields Third ArgumentTemplate

#ENDIF // ifthenenelse_hpp

Keep in mind that the Ifthenelse template is a tool: it selects one of two types according to a given Boolean constant. If this Boolean constant is true, Resultt will be defined [typedef] as the first type; in turn, Resultt is defined as the second type. Here I want specially reminded that "a alias" for a class template [typef] does not cause the C compilation device to make the body [body] of the template class instance [body]. So when we write down

TypedEf TypeName ifthenelse <(n , SQRT > :: resultt subt; SQRT And SQRT will not be completely complete. Only one of these two types will be completely limited when accessing Subt :: Result, and is an equivalent of Subt. Compared with our first method, the number of template instances that eventually caused by this strategy is compared to log2 (N): When n is quite large, this result is much smaller than the first method.

17.4 Use the summary variable [induction variables]

You may definitely say that the number of ways of the metapogram in the previous example is too embarrassed. You may also want to know, there is any way, once you encounter the problem that needs to be solved with the programming, you can save you. Oh, let's take a more "naive" but maybe more "iteration" to implement the square program of the square root.

This "naive iterative algorithm" is this: in order to calculate a square root of a value N, we only need to write a loop, the cyclic variable I increases from 1, until the square of i is more than equal to N, and the cycle is terminated. The value of the cyclic variable I at this time is the square root of N. If you write this algorithm with ordinary C , it should look like this:

INT i; for (i = 1; i * i

However, as a programs, we must construct this loop with recursive approach, and require an end condition [end criterion] to terminate recursive. This loop's meta-programming implementation version should look like this:

// meta / SQRT3.HPP

#ifndef SQRT_HPP # Define SQRT_HPP

// primary template to compute sqrt (n) via = 1> Class SQRT {public: enum {result = (i * i :: Result: I};

// Partial Specialization to End The {public: enum {result = n};}; # endif // sqrt_hpp

We realize cycles by "iteration" i in template SQRT . As long as I * i :: result as the value of the Result. Otherwise, I is what we want.

For example, if you want to calculate SQRT <16>, this template will be expanded into SQRT <16, 1>. Then, we will start a series of iterative processes, in which a variable I called "summary variable" is used, its value starts from 1. In this way, as long as I ^ 2 is smaller, we will enter the next iteration by calculating SQRT :: Result. When I ^ 2 is greater than or equal to N, I is equal to Result.

You may also want to know why you have to use a template's special version [Template Specialization] to end recursive, because the first template or late or early will always go to the step of I as a result, it seems to recurrent Termination. Here, you must emphasize it once, [The reason why the introduction of template is introduced] Is it because two branches of operators should be expanded to bring side effects. In the previous section, we have discussed this issue. Therefore, when calculating SQRT <4>, the process of compiling instruments is like this:

* STEP 1

Result = (1 * 1 <4? SQRT <4, 2> :: Result: 1 * STEP 2

Result = (1 * 1 <4? (2 * 2 <4)? SQRT <4, 3> :: Result: 2: 1 * Step 3

Result = (1 * 1 <4? (2 * 2 <4)? (3 * 3 <4)? SQRT <4, 4> :: Result: 3: 2: 1 * STEP 4

Result = (1 * 1 <4? (2 * 2 <4)? (3 * 3 <4)? 4: 3: 2: 1

Although we get the correct result in the second step, the compiler will still instantiate until a special version is found to end recursive. If there is no version of this specialization, the compiler can only be [unlimited] instantiate until the compiler itself has the limit.

Again again, using template ifthenenelse is to solve the following problems:

// meta / sqrt4.hpp

#ifndef SQRT_HPP # Define SQRT_HPP

#include "ifthenelse.hpp"

// Template to Yield Template Argument As ResultTemplate class value {public: enum {result = n};

// template to compute sqrt (n) via = 1> Class SQRT {public: // instantiate Next Step or result Type As Branch TypedEf TypeName ifthenelse <(i * i , value > :: resultt subt; // use the result = Subt :: result};

#ENDIF // SQRT_HPP

We didn't use the end condition [end criterion] here, but used a value <> template to pass the value of the template parameters to the Result.

After using the ifthenenelse <> template, the number of templates that is more than log2 (N) is not N. This is a very prominent progress for the cost of the meta-programming. Since the compiler always has certain capacity limits in the modernization of template, this method means you can calculate a larger value square root. For example, if your compiler supports 64-story nesting, you can calculate the square root of 4096 (instead of 64 square root).

This "iterative" SQRT template output is this:

SQRT <16> :: result = 4sqrt <25> :: result = 5sqrt <42> :: result = 7sqrt <1> :: result = 1

Please note that in this implementation, in this implementation, the square root is tapered (ie: 42 square root [6.48 ...] is 7 instead of 6).

17.5 Computation Integrity [Computational Completeness]

From POW <> and SQRT <> we can see that the template element programming can include the following elements:

* Status variable [state variables]: Template parameters can be this * loop structure [loop constructs]: Realize * branch selection [PATH Selection]: Using conditional expression or specialization to achieve * Integer Arithmetic ]

If the number of layers of recursive is unrestricted, the number of state variables are not bound by constraints, this technology is sufficient to calculate any "negotiable" [computable]. However, using templates to do such a calculation work is not easy. What's more, the template is currently ever need to have a compiler, and the progressive process of the emergency expansion will cause the compiler to plummet, even exhausted all available resources. Although C standard recommendations (but not forced) should be supported at least 17 layers, but complex template programs can make light breakthroughs.

Therefore, in practical applications, the use template program should be used. However, in some cases, the template is still an indispensable tool. The [Template Program] is in that it is implemented for some critical algorithm, which can be hidden into a more "traditional" template, in order to strip more efficient as much as possible.

17.6 Recreasing [Recursive Instantiation] VS Template Parameter Recursive [Recursive Template Arguments] Considering the following recursive template:

Template struct Doublify {};

Template struct trouble {typepef double1> :: longtype, Typename Trouble :: longtype,> longtype;

Template <> struct trouble <0> {typedef double longtype;};

Trouble <10> :: longtype ooch;

Use Trouble <10> :: longType not only triggered Trouble <9>, Trouble <8>, ... Trouble <0> is now available, but also Double, which is more and more complex. Table 17.1 shows how this complexity expands quickly.

As we have seen in Table 17.1, the complexity of the types of the expression Trouble :: longType is incremented in n's index level. Typically, such a condition [Recurns of template parameters] The pressure caused by the C compiler, which is much larger than the pressure of the recursive template parameters. One of the problems here is that the compiler is usually only saved for each type [MANGLE] as its representative. This type of cut is generally the exact process of encoding template in some way. An early C compiler takes a coding method [so that the internal type name length] is generally proportional to the length of the template ID. If you compile the Trouble <10> :: longtype above with this type of compile, you need more than 10,000 characters. The new point C implementation takes into account the fact that the inline template ID is often used in modern C programming, so the smart compression method is used to greatly reduce the growth of the name encoding (for example, may only use hundreds of characters. Repayed Trouble <10> :: longtype). Despite this, other problems are equivalent, so as long as it is possible, try to organize your recursive code, do not introduce the template parameters of the recursive inline.

Table 17.1. Trouble :: longtype Trouble <0> :: longtype double trouble <1> :: longtype doublify Trouble <2> :: longtype doublify , doubleify > Trouble <3> :: longtype Doublify , doubleify >, doubleify , doubleify >> 17.7 utilization Programming Implementation Cycle [Unroll Loops] One of the earliest actual applications in the programming is the decomposition cycle in numerical calculations [Press: Refers to the loop code with non-cycling to obtain higher efficiency], I show it here, As a complete example.

N-dimensional arguments or N-dimensional ports in n-dimensional array or mathematical sense are often handled in numerical applications. A typical operation is to calculate a so-called "dot product]". The bottranes of the two vectors A and B are defined as the sum of the product of these two vectors corresponding components. For example, if there are three components of two vectors, the points result is:

a [0] * b [0] a [1] * b [1] a [2] * b [2]

A math library typically provides a function to calculate such a point. Let us first consider the following direct white [strightforward] implementation:

// meta / loop1.hpp

#ifndef loop1_hpp # define loop1_hpp

Template Inline T Dot_Product (INT DIM, T * A, T * B) {t result = 0; for (int i = 0; i

[Press: This original program is not full, according to your own understanding]

When we call this function like this:

#include #include "loop1.hpp"

INT main () {Int a [3] = {1, 2, 3}; int b [3] = {5, 6, 7};

Std :: cout << "Dot_Product (3, A, B) =" << Dot_Product (3, A, B) << '/ n'; std :: coudute << "Dot_Product (3, a, a) = "<< Dot_Product (3, A, A) << '/ n';}

We will get the following results:

Dot_Product (3, A, B) = 38DOT_PRODUCT (3, A, A) = 14

This result is correct, but it takes too much time in the application that requires high efficiency. Even with this function declares that the optimization efficiency is also available. The problem is that the compiler is usually optimized to a number iteration, and this optimization is only in this example. Simply open the circulatory

a [0] * b [0] a [1] * b [1] a [2] * b [2]

It will be much better.

Of course, if we are only occasionally calculated as a few points, this [unopened] is not a problem. However, if we use this library component to execute millions of points, there is a natural day.

Of course, we can also do not call the dot_product () function, change the direct writing calculation, or we can also use special functions to calculate the amount of the special dimensional number of vectors. But these methods have a boring downtime. The template programming solves this problem for us: We "Program" to unwind the loop. Here is a primary program:

// meta / loop2.hpp

#ifndef loop2_hpp # define loop2_hpp

// Primary TemplateTemplate Class Dotproduct {public: Static T Result (t * a, t * b) {return * a * * b dotproduct :: Result (A 1, b 1);}};

// Partial Specialization As End CriteriateMplate Class DotProduct <1, T> {public: Static T Result (t * a, t * b) {RETURN * A * * B;}}

// Convenience FunctionTemplate Inline T Dot_Product (T * a, t * b) {RETURN DOTPRODUCT :: Result (A, b);}

#endif // loop2_HPP

Now, just change your app, you can get the same result:

// meta / loop2.cpp

#include #include "loop2.hpp"

INT main () {Int a [3] = {1, 2, 3}; int b [3] = {5, 6, 7};

Std :: cout << "Dot_Product <3> (A, B) =" << Dot_Product <3> (A, B) << '/ n'; std :: cout << "Dot_Product <3> (A, a) = "<< Dot_Product <3> (a, a) << '/ n';}

We don't write

DOT_PRODUCT (3, A, B)

In the generation

Dot_Product <3> (A, B)

This expression is very convenient to have a function template to translate the call into

Dotproduct <3, Int> :: Result (A, B)

This is the starting point of this element program.

In the middle program, Result is defined as the product of the first component of A and B, plus the points of the amount of the remaining components of A and B:

Template Class DotProduct {public: static t result (t * a, t * b) {return * a * * b dotproduct :: Result (A 1, B 1);}}; End conditions are when A and B are one-dimensional vector:

Template Class DotProduct <1, T> {public: static t result (t * a, t * b) {return * a * * b;}}

Therefore,

Dot_Product <3> (A, B)

In terms of, the technique is calculated in this way:

DotProduct <3, Int> :: Result (a, b) = * a * * b dotproduct <2, int> :: result (a 1, b 1) = * a * * b * (a 1) * * (B 1) DotProduct <1, int> :: result (a 2, b 2) = * a * * b * (A 1) * * (B 1) * (A 2) * * (B 2)

Note: This programming method requires dimensions to be known in the compiler, which is usually (but not necessarily) is satisfied.

Some libraries, such as Blitz (see [Blitz ]), MTL library (see [MTL]) and Pooma (see [Pooma]), is used to provide fast linear algebraic arithmetic routines. This programs are better than optimers because the former can integrate higher level [higher-level] knowledge into the operation. In fact, the various details of the library to achieve a library of industrial grade intensity [Industrial-Streg "are far more than the content we are present only and the template is only related. The unfounded consequences of the consequences are not always able to get the optimization effect of the runtime, but this has been an additional project consumption, which has exceeded the scope involved herein.

17.8 postscript

As mentioned earlier, the earliest metader program is written by erwin unruh, which is recommended by Siemens at the C Standardization Committee. He emphasized the integrity of the calculation of the model of the template and demonstrated his point of view by developing the first element program. He uses MetaWare's compiler and induces the compiler to give a string quality through a series of error messages [Error Message]. Below is the code that was permitted during the 1994 C Committee meeting (in order to compile the standard compiler, some modifications).

// meta / unruh.cpp

// prime number computation by Erwin Unruh

Template class is_prime {public: enum {prim = (p == 2) || (p% i) && is_prime <(i> 2? p: 0), i-1> :: Prim };

Template <> class is_prime <0,0> {public: enum {prote = 1};

Template <> class is_prime <0, 1> {public: enum {prim = 1};}; template Class D {public: d (void *);

Template class prime_print {// primary template for loop to print prime numbers public: prime_print a; enum {prim = is_prime :: prim}; void f () { D D = PRIM? 1: 0; AF ();}};

Template <> class prime_print <1> {// Full Specialization to End the loop public: enum {prim = 0}; void f () {d <1> d = prim? 1: 0;};

#ifndef last #define last 18 #ENDIF

INT main () {prime_print a; A.f ();}

If you compile this program, as long as the initialization D fails, the compiler will print an error message at compiling prime_print :: f (). When the initial value is 1, the above is happened. Because [D-type] only has a constructor with a void * parameter [constructor], only 0 can be legally converted [Conversion] to void *. For example, on some compiler, we (in the middle of other information) will get these error information:

Unruh.cpp: 36: Conversion from & 39; INT & 39; To Non-Scalar Type & 39; D <17> & 39; Requestedunruh.cpp: 36: Conversion from & 39; Int & 39; To Non-Scalar Type & 39; D <13> & 39; Requestedunruh.cpp: 36: Conversion from & 39; INT & 39; To Non-Scalar Type & 39; D <11> & 39; Requestedunruh.cpp: 36: Conversion from & 39; Int & 39; To Non-Scalar Type & 39; D <7> & 39; Requestedunruh.cpp: 36: Conversion from & 39; INT & 39; To Non-Scalar Type & 39; D <5> & 39; Requestedunruh.cpp: 36: Conversion from & 39; INT & 39; To Non-Scalar Type & 39; D <3> & 39; Requestedunruh.cpp: 36: Conversion from & 39; INT & 39; To Non-Scalar Type & 39; D <2> & 39; Requester

转载请注明原文地址:https://www.9cbs.com/read-101344.html

New Post(0)