2009 March 31th Tuesday (三月 三十一日 火曜日)

来源:互联网 发布:华宇软件 基金会 编辑:程序博客网 时间:2024/04/20 20:36

  After C++ experimented with the approaches described later, the C++ Standard settled upon this approach:

  For a given function name, you can have a non-template function, a template function, and an explicit specialization
template function.

  The prototype and definition for an explicit specialization should be preceded by template <> and should mention
the specialized type by name.

  A specialization overrides the regular template, and a non-template function overrides both.

  Here's how prototypes for swapping type job structures would look for these three forms:

// non-template function prototype
void Swap(job &, job &);

// template prototype
template <class Any>
void Swap(Any &, Any &);

// explicit specialization for the job type
template <> void Swap<job>(job &, job &);

  As mentioned previously, if more than one of these prototypes is present, the compiler chooses the non-template
version over explicit specializations and template versions, and it chooses an explicit specialization over a version
generated from a template.

  When the compiler uses the template to generate a function definition for a particular type, the result is termed
an instantiation of the template.

  Originally, implicit instantiation was the only way the compiler generated function definitions from templates,
but now C++ allows for explicit instantiation. That means you can instruct the compiler to create a particular
instantiation, for example, Swap<int>(), directly. The syntax is to declare the particular variety you want, using
the <> notation to indicate the type and prefixing the declaration with the keyword template:

template void Swap<int>(int, int);  // explicit instantiation

  A compiler that implements this feature will, upon seeing this declaration, use the Swap() template to generate
an instantiation using the int type. That is, this declaration means "Use the Swap() template to generate a function
definition for the int type."

  Contrast the explicit instantiation with the explicit specialization, which uses one or the other of these equivalent
declarations:

template <> void Swap<int>(int &, int &);  // explicit specialization
template <> void Swap(int &, int &);       // explicit specialization

  The difference is that these declarations mean "Don't use the Swap() template to generate a function definition.
Instead, use a separate, specialized function definition explicitly defined for the int type." These prototypes have
to be coupled with their own function definitions.

  It is an error to try to use both an explicit instantiation and an explicit specialization for the same type(s) in
the same programming unit.

  Implicit instantiations, explicit instantiations, and explicit specializations collectively are termed specializations.
What they all have in common is that they represent a function definition using specific types rather than one that
is a generic description.

  The addition of the explicit instantiation led to the new syntax of using template and template <> prefixes in
declarations to distinguish between the explicit instantiation and the explicit specialization.

  Instantiations is just a process to form a function Specializations from a template.  Specializations is a result from
an instantiation or from Specializations definitions.

...
template <class Any>
void Swap (Any &, Any &);  // template prototype

template <> void Swap<int>(job &, job &);  // explicit specialization for job
int main(void)
{
  template void Swap<char>(char &, char &);  // explicit instantiation for char
  short a, b;
  ...
  Swap(a,b);   // implicit template instantiation for short
  job n, m;
  ...
  Swap(n, m); // use explicit specialization for job
  char g, h;
  ...
   Swap(g, h);  // use explicit template instantiation for char
   ...
}

  The template in C++ language is just a variant of macro.

//arraytp.h  -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>
using namespace std;
#include <cstdlib>

template <class T, int n>
class ArrayTP
{
private:
    T ar[n];
public:
    ArrayTP() {};
    explicit ArrayTP(const T & v);
    virtual T & operator[](int i);
    virtual const T & operator[](int i) const;
};

template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
    for (int i = 0; i < n; i++)
        ar[i] = v;
}

template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
    if (i < 0 || i >= n)
    {
        cerr << "Error in array limits: " << i
            << " is out of range/n";
        exit(1);
    }
    return ar[i];
}

template <class T, int n>
const T & ArrayTP<T,n>::operator[](int i) const
{
    if (i < 0 || i >= n)
    {
        cerr << "Error in array limits: " << i
            << " is out of range/n";
        exit(1);
    }
    return ar[i];
}

#endif

  The parameter "n" has some restrictions.  It can be an integral type, an enumeration type, a reference,
or a pointer.  Thus, double m is ruled out, but double &rm and double *pm are allowed. Also, the template
code can't alter the value of the argument or take its address.  Thus, in the ArrayTP template, expressions
such as n++ or &n would not be allowed. Also, when you instantiate a template, the value used for the expression
argument should be a constant expression.

Template Specializations

  Implicit Instantiations

ArrayTb<int, 100> stuff; // implicit instantiation

  The compiler doesn't generate an implicit instantiation of the class until it needs an object:

ArrayTb<double, 30> * pt;     // a pointer, no object needed yet
pt = new ArrayTb<double, 30>; // now an object is needed

  Explicit Instantiations

  The compiler generates an explicit instantiation of a class declaration when you declare a class using the
keyword template and indicating the desired type or types. The declaration should be in the same namespace as
the template definition. For example, the declaration

template class ArrayTb<String, 100>; // generate ArrayTB<String, 100> class

  In this case the compiler generates the class definition, including method definitions, even though no object
of the class has yet been created or mentioned. Just as with the implicit instantiation, the general template is
used as a guide to generate the specialization.

  Explicit Specializations

  A specialized class template definition has the following form:

template <> class Classname<specialized-type-name> { ... };

template <> class SortedArray<char *>
{
     ...// details omitted
};

SortedArray<int> scores;    // use general definition
SortedArray<char *> dates;  // use specialized definition

  Partial Specializations

// general template
    template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
    template <class T1> class Pair<T1, int> {...};
// specialization with T1 and T2 set to int
    template <> class Pair<int, int> {...};

Pair<double, double> p1; // use general Pair template
Pair<double, int> p2;    // use Pair<T1, int> partial specialization
Pair<int, int> p3;       // use Pair<int, int> explicit specialization

// general template
    template <class T1, class T2, class T3> class Trio{...};
// specialization with T3 set to T2
    template <class T1, class T2> class Trio<T1, T2, T2> {...};
// specialization with T3 and T2 set to T1*
    template <class T1> class Trio<T1, T1*, T1*> {...};

Trio<int, short, char *> t1; // use general template
Trio<int, short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; use Trio<T1, T1*, T1*>

  Member Templates

// tempmemb.cpp -- template members
#include <iostream>
using namespace std;

template <typename T>
class beta
{
private:
    template <typename V>  // nested template class member
    class hold
    {
    private:
        V val;
    public:
        hold(V v  = 0) : val(v) {}
        void show() const { cout << val << endl; }
        V Value() const { return val; }
    };
    hold<T> q;           // template object
    hold<int> n;         // template object
public:
    beta( T t, int i) : q(t), n(i) {}
    template<typename U>   // template method
    U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
    void Show() const {q.show(); n.show();}
};
int main()
{
    beta<double> guy(3.5, 3);

    guy.Show();
    cout << guy.blab(10, 2.3) << endl;
    cout << "Done/n";
    return 0;
}

  Or another way define a memeber template.

template <typename T>
class beta
{
private:
    template <typename V>  // declaration
    class hold;
    hold<T> q;
    hold<int> n;
public:
    beta( T t, int i) : q(t), n(i) {}
    template<typename U>   // declaration
    U blab(U u, T t);
    void Show() const {q.show(); n.show();}
};
// member definition
template <typename T>
  template<typename V>
    class beta<T>::hold
    {
    private:
        V val;
    public:
        hold(V v  = 0) : val(v) {}
        void show() const { cout << val << endl; }
        V Value() const { return val; }
    };

// member definition
template <typename T>
  template <typename U>
    U beta<T>::blab(U u, T t)
    {
       return (n.Value() + q.Value()) * u / t;
    }

  The definitions have to identify T, V, and U as template parameters. Because the templates are nested, you have
to use the

template <typename T>
  template <typename V>

syntax instead of the

template<typename T, typename V>

syntax.

  Templates As Parameters

// tempparm.cpp -- template template parameters
#include <iostream>
using namespace std;
#include "stacktp.h"

template <template <typename T> class Thing>
class Crab
{
private:
    Thing<int> s1;
    Thing<double> s2;
public:
    Crab() {};
    // assumes the thing class has push() and pop() members
    bool push(int a, double x) { return s1.push(a) && s2.push(x); }
    bool pop(int & a, double & x){ return s1.pop(a) && s2.pop(x); }
};

int main()
{
    Crab<Stack> nebula;
// Stack must match template <typename T> class thing
    int ni;
    double nb;

    while (cin>> ni >> nb && ni > 0 && nb > 0)
    {
        if (!nebula.push(ni, nb))
            break;
    }

    while (nebula.pop(ni, nb))
           cout << ni << ", " << nb << endl;
    cout << "Done./n";

    return 0;
}

  The Thing<int> is instantiated as Stack<int> and Thing<double> is instantiated as Stack<double>. In short,
the template parameter Thing is replaced by whatever template type is used as a template argument in declaring
a Crab object.

原创粉丝点击