!!!Chapter 16 Templates and Generic Programming

16.1 Template Definitions

16.1.1 Defining a Function Template

A function template is a type-independant function that is used as a formula for generating a type-specific version of the function.

Template version of compare function:

// implement strcmp-like compare function
// return 0 if equal, 1 if v1 is bigger, -1 otherwise
template <typename T>
int compare (const T &v1, const T &v2)
{
    if (v1 < v2) return -1;
    if (v2 < v1) return 1;
    return 0;
}
A template definition starts with the keyword template followed by a template parameter list, which is a comma-separated list of one or more template parameter bracketed by the less-than (<) and greater-than (>) tokens.
The template parameter list cannot be empty.

Template Parameter List

The template parameter list acts much like a function parameter list. Template parameters represent types or values we can use in the definition of a class or function.

A template parameter can be a type parameter, which represents a type, or anontype parameter, which represents a constant expression(16.1.5 P 632).

A type parameter is defined following the keyword class or typename.

Using a Function Template

When we use a function template, the compiler infers what template arguments to bind to the template parameters. Once the compiler determines the actual template argument, itinstantiates an instance of the function template for us.

int main()
{
    // T is int; 
    // compiler instantiates int compare (const int&, const int&)
    cout << compare(1, 0) << endl;
    // T is string;
    // compiler instantiates int compare (const string&, const string&)
    string s1 = "hi", s2 = "world";
    cout << compare(s1, s2) << endl;
    return 0;
}

inline Function Templates

A function template can be declared inline in the same way as a nontemplate function. the specifier is placed following the template parameter list and before the return type:

// ok: inline specifier follows template parameter list
template <typename T> inline T min(const T&, const T&);
// error: incorrect placement of inline specifier
inline template <typename T> T min(const T&, const T&);

16.1.2 Defining a Class Template

Defining our own Queue class template.

template <class Type> class Queue {
public:
    Queue();               // default constructor
    Type &front();         // return element from head of Queue
    const Type &front() const;
    void push(const Type &);    //add element to back of Queue
    void pop();                 //remove element from head of Queue
    bool empty() const;         // true if no elements in the Queue
private:
    // ....
};

Using a Class Template

In contrast to calling a function template, when we use a class template, we must explicitly specify arguments for the template parameters:

Queue<int> qi;
Queue<vector<double> > qc;
Queue<string> qs;
The compiler uses the arguments to instantiate a type-specific version of the class.

16.1.3 Template Parameters

When we wish to use the type or value that a template parameter represents, we use the same name as the corresponding template parameter.

Template Parameter Scope

The name of a template parameter can be used after it has been declared as a template parameter and until the end of the template declaration or definition.

A template parameter with the same name as an object, function, or type declared in global scope hides the global name:

typedef double T;
template <class T> T calc (const T &a, const T &b)
{
    // tmp has the type of the template parameter T not the global typedef
}

Restriction on the Use of a Template Parameter Name

A name used as a template parameter may not be reused within the template:

template <class T> T calc (const T &a, const T &b)
{
    typedef double T;    //error: redeclares template paramter T
}
The restriction also means the name of a template parameter can be used only once within the same template parameter list:

// error: illegal reuse of template parameter name
template <class V, class V> V calc(const V&, const V&);
The name of a template parameter can be reused across different templates

Template Declarations

We can declare a template without defining it:

// declares compare but does not define it
template <class T> int compare (const T&, const T&);
The names of the template parameters need not be same across declarations and definitions of the same template:

// all three calc refer to the same function template
template <class T> T calc(const T&);
template <class U> U calc(const U&);
template <class Type> Type calc(const Type&) {...}
Each template type parameter must be preceded either by the keyword class or typename; each nontype parameter must be preceded by a type name:

// error: must precede U by either typename or class
template <typename T, U> T calc (const T&, const U&);

16.1.4 Template Type Parameters

A template type parameter can be used as a type specifier any where in the template, in exactly the same way as a built-in or class type specifier.

Distinction Between typename and class

In a function template parameter list, the keywords typename and class have the same meaning and can be used interchangeably.

The keyword typename was added to C++ as part of Standard C++, so older programs are more likely to use the keyword class exclusively.

Designating Types inside the Template Definition

In addition to defining data or function members, a class may define type members.

When we want to use such types inside a function template, we must tell the compiler that the name we are using refers to a type.

template <class Parm, class U>
Parm fcn (Parm * array, U value)
{
    Parm::size_type * p;    // if Parm::size_type is a type, then a declaration
                            // if Parm::size_type is an object, then multiplication
}
We know that size_type must be a member of the type bound to Parm, but we don't know whether size_type is the name of a type or a data member. By default, the compiler assumes that such names name data members, not type.
If we want the compiler to treat size_type as a type, then we must explicitly tell the compiler to do so:

template <class Parm, class U>
Parm fcn(Parm* array, U value)
{
    typename Parm::size_type * P;    // ok:declares p to be a pointer
}
We tell the compiler to treat a member as a type by prefixing uses of the member name with the keyword typename.

Of course, this declaration puts an obligation on the types used to instantiate fcn: Those types must have a member named size_type that is a type.

16.1.5 Nontype Template Parameters

Nontype parameters are replaced by values when the function is called. The type of that value is sepcified in the template parameterlist.

// initialize elements of an array to zero
template <class T, size_t N> void array_init(T (&parm) [N])
{
    for (size_t i = 0; i != N; ++i) {
        parm[i]=0;
    }
}
A template nontype parameter is a constant value inside the template definition.

When array_init is called, the compiler figures out the value of the nontype parameter from the array argument:

int x[42];
double y[10];
array_init(x);    // instantiates array_init (int (&) [42])
array_init(y);    // inst5antiate array_init (double (&) [10])

Type Equivalence and Nontype Parameters

Expressions that evaluate to the same value are considered equivalent template arguments for a template nontype parameter:

int x[42];
const int sz = 40;
int y[sz + 2];
array_init(x);    // instantiates array_init (int (&)[42])
array_init(y);    // equivalent instantiation

16.1.6 Writing Generic Programs

The template code always makes some assumptions about the types that will be used. For example, thecomparetemplate assumes the type should support less-than operation.

The operations performed inside a function template constrains the types that can be used to instantiate the function. It is up to the programmer to guarantee that the types used as the function arguments actually support any operations that are used, and that those operations behave correctly in the context in which the template uses them.

Writing Type-Independent Code

When writing template code, it is useful to keep the number of requirements placed on the argument types as small as possible.

16.2 Instantiation

The process of generating a type-specific instance of a template is known as instantiation.

Instantiating a Class

When we write:

Queue<int> qi;
the compiler automatically creates a class named Queue<int>.

Each instantiation of a class template constitutes an independent class type. The Queue instantiation for the type int has no relationship to nor any special access to the members of any other Queue type.

Class Template Arguments Are Required

A class template does not define a type; only a specific instantiation defines a type.

For example, Queue is not a type, Queue<int> or Queue<string> are.

Function-Template Instantiation

When we use a function template, the compiler will usually infer the template arguments for us:

int main()
{
    compare(1,0);      // ok:binds template parameter to int
    compare(3.14, 2.7) // ok: binds template parameter to double
}
This program will instantiates two versions of compare.

16.2.1 Template Argument Deduction

The process of determing the types and values of the template arguments from the type of the function arguments is calledtemplate argument deduction.

Multiple Type Parameter Arguments Must Match Exactly

A template type parameter may be used as the type of more than one function parameter. In such case, the deduced types must match:

template <typename T>
int compare (const T& v1, const T& v2)   {...}

int main()
{
    short si;
    // error: cannot instantiate compare(short, int)
    compare (si, 1024);
    return 0;
}
If the designer of compare wants to allow normal conversions on the arguments, then the function must be defined with two type parameters:

// argument types can differ, but must be compatible
template <typename A, typename B>
int compare(const A& v1, const B& v2) {...} 
A < operator must exist that can compare values of those types.

Limited Conversions on Type Parameter Arguments

In general, arguments are not converted to match an existing instantiation (egP 638); in stead, a new instance is generated.

There are only two kinds of conversions that the compiler will perform rather than generating a new instantiation(P 639):

1. const conversion.

2. array or function to pointer conversion.

E.G:

template <typename T> T fobj (T,T);
template <typename T> T fref (const T&, const T&);
string s1("a value");
const string s2("another value");
fobj(s1, s2);        // ok:calls f(string, string), const is ignored
fref(s1, s2);        // ok: nonconst object s1 converted to const reference

int a[10], b[42];
fobj(a,b);           // ok: calls f(int *, int*)
fref(a,b);           // error: array types don't match; arguments aren't converted to pointers

Normal Conversion Apply for Nontemplate Arguments

The restriction on type conversion applies only to those arguments whose types are template parameters.

template <class Type> Type sum (const Type &op1, int op2) { ... }
Because the type of op2 is fixed, we can pass int, double... as the second argument.

Template Argument Deduction and Function Pointers





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值