Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

Even for practical purposes, theory generally turns out the most important thing in the end.

—Oliver Wendell Holmes

In the previous chapter, we saw what you could do with C++/CLI generics. In this chapter, I will introduce templates, which are similar to generics in many ways and different in many others.

Comparison with Generics

Templates and generics both allow you to create functions and classes that implement functionality for multiple types. We can approach this in one of two ways:

  • Write code that is decoupled from the type of data that is being acted upon. The code only implements algorithms that do not depend on the data type, so the algorithms act on the data in a generic fashion. This is the idea behind CLI generics.

  • Write generalized high-level code that the compiler adapts for each type of data. The result is a separate set of data-dependent methods and types for each type of data that is acted upon. This is the idea behind C++ templates.

Generics types are decoupled from the data types until execution, and templates are also decoupled but bound by the compiler to specific data types during the compilation. The result of a generic method is a single method specified in the IL; the result of a template method is one method generated in the IL for each combination of type arguments passed to the template during compilation.

Note

Generics is a feature supported by the CLR, and generics is implemented in a variety of .NET languages including C#. In Visual Studio 2013, however, templates are unique to C++.

Because generic methods and classes are ignorant of their type parameters, they can easily be defined in one module, be referenced in another module defining a new type, and still be used on that type. For example, the generic Stack<T> class in System.Collections.Generics can be used to manage a stack of any number of user-defined types.

Template methods and types, although defined in a general fashion, depend on their type parameters and require a level of runtime compilation that does not exist in the CLI to operate on types defined elsewhere. For this reason, templates are compilation-unit specific.

The requirement that generic methods and classes be completely ignorant of their type parameters places severe restrictions on generics. Generic constraints help a little, in that they allow the generic types to make some assumptions about the possible type parameters and to act only on type parameters that implement a given set of interfaces.

Templates have no such restriction. A template can be applied to any type, and the compiler can determine if a syntax error is generated by an invalid type parameter. Type parameters are even able to fail during the initial substitution phase, and rather than aborting the compilation, the compiler continues to look for an appropriate template candidate for substitution without generating a syntax error. Templates can also have parameters that are not types, called nontype template parameters, and parameters that are templates themselves, called template template parameters.

Where generic types represent an efficient way to reduce code by allowing the programmer to write a single method that applies to multiple types, templates emerge as a powerful C++ metaprogramming environment and allow the programmer to take advantage of the compilation process to implement sophisticated algorithms and language extensions at compile time.

Syntactic Differences

These two type-parameter mechanisms have great a deal in common and can often be used interchangeably. The similarity of templates and generics in terms of functionality is reflected in the C++/CLI syntax. Switching from one to the other is often as simple as changing the generic keyword to template, or vice versa.

Consider the following line:

generic <typename T> ref class TypeGeneric {};

TypeGeneric <T> is a complete generic type capable of runtime creation. Now consider this line:

template <typename T> ref class TypeTemplate {};

TypeTemplate <T> is not a type at all. It is a model for a type, which can be generated by the compiler when it is used or instantiated later on in the compilation process.

Comparison Summary

While templates and generics are similar in many ways, there is one major difference between the two mechanisms. Unlike generic types, which are instantiated by the Virtual Execution System (VES) at runtime, templates are fully resolved, instantiated, and compiled by the C++ compiler. Generic types can be thought of as a packaging mechanism for CLI types, whereas templates act more like smart macros that are expanded, verified, and executed at compile time.

Compilation Process

When the compiler encounters a generic function, it verifies that it makes sense for all possible types given its constraints and implementation. A single function is then generated in the IL.

Consider the following simple generic function:

generic <typename T>

ref class Test

{

public:

    Test()

    {

        System::Console::WriteLine(this->ToString());

    }

};

void main()

{

    Test<int> i;

    Test<float> j;

}

When the class Test is parsed, IL and metadata are created for a generic class. Let’s compile and run it:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

Test`1[System.Int32]

Test`1[System.Single]

Now let’s run ildasm.exe on it. In the ILDasm main window, open the constructor (.ctor) definition. You should find IL and metadata similar to the following:

.method public hidebysig specialname rtspecialname

        instance void  .ctor() cil managed

{

  // Code size       18 (0x12)

  .maxstack  1

  IL_0000:  ldarg.0

  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()

  IL_0006:  ldarg.0

  IL_0007:  callvirt   instance string [mscorlib]System.Object::ToString()

  IL_000c:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0011:  ret

} // end of method Test`1::.ctor

The compiler created a method named Test`1::.ctor, which is the constructor of a class named Test with a single generic parameter. Note that, in this case, Test<int> and Test<float> have nothing to do with the constructor. The constructor for Test<T> is independent of the type parameter.

On the other hand, take the exact same example and substitute the keyword generic for template. Compile and run it:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

Test<int>

Test<float>

As you can see, two completely different classes, Test<int> and Test<float>, are created. Let’s run ildasm.exe on this and have a look (see Figure 15-1). Note that Test<float> and Test<int> are distinct.

Figure 15-1.
figure 1figure 1

Metadata for the different compile-time–generated template instantiations

Now let’s have a look at the code, along with a description of how the compiler parses it:

01: template <typename T>

02: ref class Test

03: {

04: public:

05:     Test()

06:     {

07:         System::Console::WriteLine(this->ToString());

08:     }

09: };

10: void main()

11: {

12:     Test<int> i;

13:     Test<float> j;

14: }

By line 9, the compiler has recognized that there is a template. It then figures out which types are determinate and indeterminate, binds any reference it can, issues any detectable syntax errors, and files the template away for future instantiation.

If a generic were used, by line 9, the compiler would have enough information to commit the class to metadata. With a template, it is just a potential class until it is instantiated. The template tells the compiler how to make a certain kind of class if you decide to make one.

Now back to parsing. On line 12, the compiler attempts to instantiate Test<int>. The template is reparsed, int is substituted for typename T, any unbound references are bound, and the class is generated. The compiler is now ready to commit Test<int> to metadata. Likewise, on line 13, the compiler is able to parse and determine whether Test<float> is valid.

Note

The CLR requires that generics are designed to work for all possible types meeting any given type restraints, whereas templates only need to be designed to work with the types you actually instantiate.

Now open the Test<int> constructor (.ctor) definition. You should find metadata and IL similar to the following:

.method public hidebysig specialname rtspecialname

        instance void  .ctor() cil managed

{

  // Code size       18 (0x12)

  .maxstack  1

  IL_0000:  ldarg.0

  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()

  IL_0006:  ldarg.0

  IL_0007:  callvirt   instance string [mscorlib]System.Object::ToString()

  IL_000c:  call       void [mscorlib]System.Console::WriteLine(string)

  IL_0011:  ret

} // end of method 'Test<int>'::.ctor

A quick comparison shows you that the metadata and IL for the method of the template class and the method of the generic class are identical. In this case, it seems to make more sense to use a generic, because the int and float cases are duplicates. In actuality, the performance implications are unclear, because the CLR creates duplicate instantiations for value types and creates a single, common instantiation for reference types.

Templates Don’t Really Exist

Since templates are instantiated into regular CLI types and methods, from the CLI perspective, they don’t exist as a special entity. The CLR supports generics explicitly, meaning that it supports IL packaging types as generic parameters. There is no specific IL for templates, and it’s easy to see why.

The compiler sees a template as a set of instructions for creating a class based on type parameters. Once the types are instantiated with template arguments, the compiler knows exactly what kind of class to create, and this class is written to metadata and IL specific to the types used in the instantiation. Only instances of the template, not the template itself, make it into the module. Therefore, templates cannot be exported from an assembly like generics, and the only way for a programmer to reuse a template is to reuse the source code definition of that template.

EXERCISE

In theory, you could create a template generic, though this is not yet a part of the C++/CLI specification. Can you think of a compelling reason to add this feature?

Constraining Freedom Is a Good Thing

To make the generics feature somewhat useful, being able to restrict the types in some fashion became necessary. This is called generic constraints. Writing something using a completely arbitrary type that is in any way useful is very difficult. After all, what could you do with it? Even the previous sample routine relies on the fact that all CLR types support an interface that has the ToString() method. Without that implicit constraint, even the previous simple example would not compile. Therefore, constraints restrict, at compile time, the kinds of types that may be used as generic type arguments and, equivalently, tell the generic what kinds of types it needs to support. The typical paradigm is that you have a particular interface that you want to use, so you constrain your generic type to derive from that interface. I’ll show an example later in the chapter.

Unlike generics, templates do not support explicit type constraints. Because generics are instantiated by the VES at runtime, they require a compile-time mechanism to prevent instantiation of a generic type with invalid type arguments, and generic type constraints are that mechanism. On the other hand, templates are resolved and instantiated by the compiler at compile time, and thus do not suffer from the invalid runtime instantiation problem. It can still be argued that some forms of explicit type constraints may be useful when used with templates, but, as a matter of design, C++ makes use of the type constraints implicitly present in the template code instead.

The Template Paradigm

Generic types are designed to work under the following paradigms:

  • You only call methods from System::Object.

  • You use a constraint to restrict the possible type parameters and only use methods enabled by the constraint (see Chapter 16).

  • You use a cast operator to perform a runtime switch on the type parameter and to perform type-specific operations.

The basic idea is that generics either have to work for all possible types given the restraints, which means either System::Object or any constraint you define, or the types have to be forked and processed at compile time.

Templates are broader. Rather than the compiler checking that the class or method will work for all or a class of type parameters when it is compiled, the compiler only checks that it is possible for some type; that is, the template is preparsed for syntax errors.

When a template is instantiated with a particular set of type parameters, the template is reevaluated in this context and compiles into code that is specific for the type parameters.

Specialization

A template may be specialized by redefining it with a more-specific set of type parameters. The template engine favors the most-specialized version during compilation. For example, if you wanted to make a default class template for all types, you would write the following:

template <typename T> ref class R {};

If you later decided that you wanted to handle a certain type, such as a double, in a custom fashion, you would declare

template<> ref struct R<double> {};

A more-complicated example using a template function and a nontemplate function overload follows:

using namespace System;

template <typename T> void function(T t)

{

    Object ^o = t;

    Console::WriteLine(o->GetType());

};

template<> void function(int i)

{

    Console::WriteLine("template integer");

};

void function(int j)

{

    Console::WriteLine("not a template");

}

void main()

{

    String ^s="hello";

    function(s);

    function(3.0);

    function(1);

    function<int>(1);

}

In this example, we declare a template function, appropriately named function(). The primary template function displays the type name of the type parameter on the screen. There are specializations for an integer type parameter and a final nontemplate function with the same function name. In the main() routine, we use the compiler’s ability to deduce the type of the arguments to call the correct template function.

Let’s compile and run this sample:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

System.String

System.Double

not a template

template integer

In this case, the calls to function() with String and double are routed to the primary template. The first call to function() passing an integer goes to the nontemplate version, and the call that explicitly specified function<int>() is sent to the explicit specialization with int.

One important thing to note is the explicit boxing in the primary template. In the code, I wrote the following:

Object ^o = t;

Console::WriteLine(o->GetType());

rather than

Console::WriteLine(t->GetType());

Unlike generic types, which merge the reference and value-type syntax, templates are literal parses of the code. In this case, we want the template to work with both value and reference types. If it’s a handle, the following syntax is correct:

t->GetType()

If it’s a value type (or a reference type on the stack), the following line is the correct syntax:

t.GetType()

We can take care of this difference in syntax by either converting to System::Object^, as shown in the example, or by using a partial specialization for reference types, as I will show next.

Partial Specialization

A declaration of a class template that partially qualifies its template parameters is called a partial specialization. Partial specializations are still templates, not instantiations or explicit specializations, as they need to be instantiated with full type parameters before code is generated. Template parameters may be specialized by pointer indirection, type, duplication of parameters, and in other ways, as described in the C++ standard.

Consider the following example:

using namespace System;

template <typename T, typename W> ref struct R

{

    static R()

    {

        Console::WriteLine("T,W: "+__FUNCTION__);

    }

};

template <typename T, typename W> ref struct R<T^,W>

{

    static R()

    {

        Console::WriteLine("T^,W: "+__FUNCTION__);

    }

};

template <typename T, typename W> ref struct R<T*,W>

{

    static R()

    {

        Console::WriteLine("T*,W: "+__FUNCTION__);

    }

};

template <typename T> ref struct R<T,T>

{

    static R()

    {

        Console::WriteLine("T,T: "+__FUNCTION__);

    }

};

void main()

{

    R<int,double> Primary;

    R<R^,int> First;

    R<int,int> Second;

    R<char*,int> Third;

}

We have a primary template and three partial specializations. The first specialization requires the first type parameter to be some kind of reference type, and the second specialization requires both parameters to be the same. The third specialization requires a pointer to a native type.

Let’s compile and run this:

C:\>cl /nologo /clr:pure test.cpp

test.cpp

C:\>test

T,W: R<int,double>::R (static class constructor)

T^,W: R<struct R ^,int>::R (static class constructor)

T,T: R<int,int>::R (static class constructor)

T*,W: R<char *,int>::R (static class constructor)

As you can see from the results, the primary template and each of the two partial specializations are instantiated in turn.

Partial Ordering of Function Templates

What if you wanted to distinguish functions in a manner similar to partial specialization? Suppose, for example, you wanted to replace ToString() with a custom version that formats strings for your built-in variables in a certain fashion; perhaps you want to display all doubles in scientific notation:

using namespace System;

template <typename T> String ^MyToString(T t)

{

    return t.ToString();

}

template <typename T> String ^MyToString(T ^t)

{

    return t->ToString();

}

template <> String ^MyToString(double d)

{

    return d.ToString("0.###e+00");

}

value struct V

{

    virtual String ^ToString() override

    {

        return "it's V";

    }

};

void main()

{

    V v;

    int i = 23;

    double d=47.3;

    Console::WriteLine(MyToString<int>(i));

    Console::WriteLine(MyToString<double>(d));

    Console::WriteLine(MyToString<V>(v));

}

In this example, we provide a template for our most general case:

template <typename T> String ^MyToString(T t) {}

and an overload that is more specific:

template <typename T> String ^MyToString(T ^t) {}

When we invoke MyToString(), the compiler picks out the more explicit or restrictive match by a process known as partial ordering during overload selection. Partial ordering allows us to distinguish between a typical value type being passed by value and a typical reference type being passed by handle.

Let’s compile and run this:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

23

4.73e+01

it's V

In this case, we are still using ToString(), which is a method from System::Object, for our example. Unlike a generic type, the template compiles even if it does not recognize the method to be called, as long as it is not resolvable without the determination of the type parameter. For example, consider the following:

using namespace System;

template <typename T> String ^MyToString(T ^t)

{

    return t->MyFunction();

}

void main() {}

This program compiles fine even though the compiler is unable to determine whether the hypothetical handle T^t has a member method called MyFunction(). Only when the user attempts to instantiate the template using a real type parameter can the compiler determine success or failure. Generic types assume the worst; templates hope for the best.

Let’s now complete this program, realizing the template’s hopes:

using namespace System;

template <typename T> String ^MyToString(T ^t)

{

    return t->MyFunction();

}

ref struct R

{

    String ^ MyFunction()

    {

        return "Hello";

    }

};

void main()

{

    R ^r = gcnew R();

    Console::WriteLine(MyToString(r));

}

Now let’s compile and run it:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

Hello

And that is correct.

Nontype Template Parameters

One of the primary advantages of templates is the ability to have template parameters that are not types; they can be integer values, for example.

Fibonacci Number Template

Consider the following template, which generates Fibonacci numbers:

using namespace System;

template <int N>

struct Fibonacci

{

    enum { Value = Fibonacci<N-1>::Value + Fibonacci<N-2>::Value};

};

template <>

struct Fibonacci<0>

{

    enum {Value = 0 };

};

template <>

struct Fibonacci<1>

{

    enum {Value = 1 };

};

void main()

{

    Console::WriteLine(Fibonacci<7>::Value);

}

This code uses an enumeration value to set a number within a class; then it uses a recursive algorithm with two explicit specializations to calculate a Fibonacci number.

Let’s compile and run it:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

13

The interesting thing about this program is that it allows the value of the template to be calculated at compile time because of the way that it is written. If we look at the program in ildasm, we see that its entire recursive nature of the algorithm was resolved, and the code was distilled down to:

int main()

{

    Console::WriteLine(13);

    return 0;

}

Square Roots Template

Let’s try a more-complicated example. This example calculates the rounded square root of an integer. It uses default values in the template parameters, which is permitted in C++:

using namespace System;

template <int N, int low=1, int high=N>

struct Root

{

    enum { Value = Root<N, ((low+high)/2), N/((low+high)/2)>::Value};

};

template <int N, int R>

struct Root<N,R,R>

{

    enum {Value = R};

};

void main()

{

    Console::WriteLine(Root<196>::Value);

}

Let’s compile and run it:

C:\>cl /nologo /clr:pure test.cpp

C:\>test

14

This program attempts to calculate the square root iteratively by continually averaging a guess and the number divided by the guess. The square root is always somewhere in between the two. The program is only guaranteed to work if the input is a perfect square, or else a compilation error may result.

Prime Numbers Template

In 1994, Erwin Unruh distributed a program at a C++ standards committee meeting to print out prime numbers as error messages using templates. Although the original program does not issue the same diagnostics on Visual C++ 2005, here is a version I wrote that has the same effect:

template <int p, int i=p-1> struct is_prime

{

    enum { value = (p%i) && is_prime<p,i-1>::value };

};

template<int p> struct is_prime<p,1>

{

    enum { value=1 };

};

template<> struct is_prime<2>

{

    enum { value=1 };

};

template <int p> ref struct Prime_print

{

    Prime_print<p-1> a;

    static void *ptr = (int)is_prime<p>::value;

};

template<> ref struct Prime_print<1> {};

void main()

{

    Prime_print<9> d;

}

Let’s compile and run this:

C:\>cl  /nologo /clr:pure test.cpp

test.cpp

test.cpp(16) : error C2440: 'initializing' : cannot convert from 'int' to 'void *'

        Conversion from integral type to pointer type requires

reinterpret_cast, C-style cast or function-style cast

        This diagnostic occurred in the compiler generated function

'void Prime_print<p>::Prime_print(void)'

        with

        [

            p=2

        ]

test.cpp(16) : error C2440: 'initializing' : cannot convert from 'int' to 'void *'

        Conversion from integral type to pointer type requires

reinterpret_cast, C-style cast or function-style cast

        This diagnostic occurred in the compiler generated function

'void Prime_print<p>::Prime_print(void)'

        with

        [

            p=3

        ]

test.cpp(16) : error C2440: 'initializing' : cannot convert from 'int' to 'void *'

        Conversion from integral type to pointer type requires

reinterpret_cast, C-style cast or function-style cast

        This diagnostic occurred in the compiler generated function

'void Prime_print<p>::Prime_print(void)'

        with

        [

            p=5

        ]

test.cpp(16) : error C2440: 'initializing' : cannot convert from 'int' to 'void *'

        Conversion from integral type to pointer type requires

reinterpret_cast, C-style cast or function-style cast

        This diagnostic occurred in the compiler generated function

'void Prime_print<p>::Prime_print(void)'

        with

        [

            p=7

        ]

As you can see, all primes less than or equal to the nontype template parameter are displayed in the diagnostics.

Complex Numbers

In Chapter 11, we did several interesting things:

  • We defined a complex number class.

  • We defined a class of integers modulo 13.

  • We introduced the fact that the Fibonacci numbers can be calculated using the golden ratio.

In this section, we will gather together these ostensibly different concepts into a single program that calculates Fibonacci numbers modulo any prime using the golden ratio formula. This program will use templates, type and nontype parameters, and specializations in order to create clean, modular code.

Overview

It might seem odd that to use a complex number class in association with the Fibonacci number formula, but it all depends on our definition of complex number pairs. Let’s generalize what it means to be a complex number by considering pairs (a, b) where the second number is multiplied by the square root of 5 instead of the square root of –1. In fact, we can use a nontype template parameter of type integer to indicate what number is under the square root. Our complex number class will also have a type parameter to indicate the type of each number in our pair (a,b). We will use this type parameter to allow us to feed the class type of Modulo<P>, where Modulo is our class template for numbers modulo a prime P, and P is the nontype template parameter.

Mathematical Formulas

It is a mathematical fact that the sequence of Fibonacci numbers can be produced from powers of the golden ratio and its conjugate. The specific formulas follow:

As stated previously, our Complex class template is able to use pairs of arbitrary types, where the number inside the radical is an arbitrary integer, such as 5. To do this, we use a nontype template parameter whose nontype template arguments are integer constants, as well as another type parameter for the underlying type of the pair:

template <typename T, int N> value struct Complex;

In our complex number class in Chapter 11, we used two doubles as our private data members, corresponding to the real and imaginary parts of the complex number. The imaginary part was the double that was implicitly multiplied by the square root of –1 to create the complex number. In this chapter’s class, we will also use a real and an imaginary part of an arbitrary type and consider the part that is multiplied by the integer under the square root of the imaginary part.

We will implement the complex number class using 5 as the radix to create the golden ratio and its complement for the Fibonacci formula. But again, why stop there? Rather than using doubles, why not use integers modulo a prime for our numbers? Having types with operators for the basic mathematical operations allows us to do just that.

So we’re going to base our complex numbers on types other than int, such as float and double. It seems like a natural task for generics, right? In order to implement a generic complex number class, all we have to do is to be able to add, subtract, multiply, and divide these built-in types. That is an ostensibly simple thing to do but looks can be deceiving.

Generic Implementation

Generic operations are all defined in terms of virtual function calls in the IL. Therefore, when generics are parsed, the compiler has to be able to figure out which functions to call for a particular expression. If there were a common multiply interface that all types supported, then generic classes would be able to call a multiply function for the arbitrary types and resolve the expression. As it is, there is no common arithmetic interface for all types, for two reasons:

  • Arithmetic doesn’t always have any meaning for an arbitrary type that is descended from Object^. For example, what does String^ multiplied by String^ mean?

  • Implementing extra interfaces for arbitrary types is burdensome to programmers.

Of course, it is also impossible to rewrite retroactively the built-in types to support an arithmetic interface, so we are faced with the unpleasant task of creating a wrapper class for each of the built-in types that support the kind of arithmetic interface required to implement a Complex variable based on an arbitrary built-in type. We could then constrain the types to derive from the arithmetic interface. This solution is neither elegant nor optimal.

Templates to the Rescue

Implementation using class templates is as straightforward as it is efficient. The compiler can determine at compile time that it already knows how to multiply two items of type float. When the template is instantiated, the types are not a mystery to the compiler, so it is able to produce the correct IL in each case. There is no need to create a single function that is able to handle arbitrary types. In fact, the instantiations may produce completely different code based on the definitions of the types themselves!

Without further ado, here are the Complex<typename T,int N>, Modulo<int P>, and Fibonacci<typename T> classes; it’s probably the longest example in the book, but I think it’s worth it:

using namespace System;

template <int P> String^ Radix()

{

    return " sqr("+P.ToString()+")";

}

template <> String^ Radix<-1>()

{

    return "i";

}

template <typename T, int N>

value struct Complex

{

    T re;

    T im;

    Complex(T _re, T _im)

    {

        re = _re;

        im = _im;

    }

    Complex(T _re)

    {

        re = _re;

        im = T(0);

    }

    static Complex operator* (Complex lhs, Complex rhs)

    {

        return Complex(lhs.re*rhs.re + (lhs.im*rhs.im*T(N)),

            lhs.re*rhs.im + lhs.im*rhs.re);

    }

    static Complex operator/ (Complex lhs, Complex rhs)

    {

        T bottom = rhs.re*rhs.re - rhs.im*rhs.im*T(N);

        Complex product = Complex(lhs * ∼rhs);

        return Complex(product.re/bottom, product.im/bottom);

    }

    static Complex operator+ (Complex lhs, Complex rhs)

    {

        return Complex(lhs.re+rhs.re, lhs.im+rhs.im);

    }

    static Complex operator- (Complex lhs, Complex rhs)

    {

        return Complex(lhs.re-rhs.re, lhs.im-rhs.im);

    }

    static Complex operator∼ (Complex lhs)

    {

        return Complex(lhs.re, -lhs.im);

    }

    virtual String^ ToString() override

    {

        return re.ToString() + (!!im ? " + " + im.ToString() +  Radix<N>() : "");

    }

};

template <int P>

value struct Modulo

{

    int Value;

    Modulo(int Val)

    {

        Value = Val % P;

    }

    static bool operator!(Modulo rhs)

    {

        return !rhs.Value;

    }

    static void ExtendedEuclid(int a, int b, int %d, int %x, int %y)

    {

        if(b==0)

        {

            d=a;

            x=1;

            y=0;

        }

        else

        {

            ExtendedEuclid(b,a%b, d, y, x);

            y-= (a/b)*x;

        }

    }

    static bool operator==(Modulo lhs, Modulo rhs)

    {

        return((rhs.Value-lhs.Value) % P == 0);

    }

    static bool operator!=(Modulo lhs, Modulo rhs)

    {

        return !(lhs == rhs);

    }

    static Modulo operator* (Modulo lhs, Modulo rhs)

    {

        return Modulo((lhs.Value * rhs.Value) % P);

    }

    static Modulo operator+ (Modulo lhs, Modulo rhs)

    {

        return Modulo((lhs.Value + rhs.Value) % P);

    }

    static Modulo operator- (Modulo lhs, Modulo rhs)

    {

        return Modulo((lhs.Value - rhs.Value) % P);

    }

    static Modulo operator- (Modulo lhs)

    {

        return Modulo((P - lhs.Value) % P);

    }

    static Modulo operator/ (Modulo lhs, Modulo rhs)

    {

        int d, x, y;

        ExtendedEuclid(rhs.Value,P,d,x,y);

        return lhs*Modulo(x*d);

    }

    virtual String ^ ToString() override

    {

        Value = (Value+P) % P;

        String ^s = Value.ToString();

        return s;

    }

};

template <typename T> ref struct Fibonacci

{

    static T half = T(1)/T(2);

    static Complex<T,5> phi = Complex<T,5>(half,half);

    static Complex<T,5> theta = Complex<T,5>(half,-half);

    static Complex<T,5> difference = phi-theta;

    template <int N>

    ref struct Result

    {

        static initonly Complex<T,5> phi_n = Result<N-1>::phi_n * phi;

        static initonly Complex<T,5> theta_n = Result<N-1>::theta_n * theta;

        static initonly Complex<T,5> Value = (phi_n-theta_n)/difference;

    };

    template <>

    ref struct Result<0>

    {

        static initonly Complex<T,5> phi_n = Complex<T,5>(T(1));

        static initonly Complex<T,5> theta_n = Complex<T,5>(T(1));

        static initonly Complex<T,5> Value = Complex<T,5>(T(0));

    };

    template <int N>

    static void Print()

    {

        Print<N-1>();

        Console::Write("{0,4} ", Result<N>::Value);

    }

    template <>

    static void Print<0>()

    {

    }

};

void main()

{

    Fibonacci<double>::Print<14>();

    Console::WriteLine();

    Fibonacci<Modulo<7>>::Print<14>();

    Console::WriteLine();

    Fibonacci<Modulo<13>>::Print<14>();

    Console::WriteLine();

}

C:\>cl /clr:safe /nologo test.cpp

C:\>test

   1    1    2    3    5    8   13   21   34   55   89  144  233  377

   1    1    2    3    5    1    6    0    6    6    5    4    2    6

   1    1    2    3    5    8    0    8    8    3   11    1   12    0

Note that the 7th Fibonacci number is (–1 modulo 7), and the 13th is (–1 modulo 13). In fact, for prime numbers p other than 5, the pth Fibonacci number is always equal to either 1 or –1 modulo p.

Core Differences: Templates and Generics

Templates and generics have the following core differences:

  • Generics stay generic until the type substitution is performed at runtime. Templates are specialized at compile time.

  • The CLR specifically supports generic types in metadata and IL. Template’s types are resolved into ordinary types at compile time.

  • The compiler generates a single class or function for all types. Templates generate different classes and functions for each specialized parameter list.

  • Generics specialized in different assemblies with identical type parameters are the same generic type; template types are unique inside a compilation and cannot be exported outside assemblies.

  • Templates alone allow nontype parameters.

  • Templates alone allow type parameters to have default values.

  • Templates allow partial and explicit specialization.

  • Templates support template template parameters.

  • Template classes can have type parameters as base classes.

Summary

Templates seem straightforward at first, but the details and ramifications of the implementation boggle the mind. Having this exposure to templates sets us up well to take another look at generic types, which we will do in the next chapter.