C++类模板

本文详细介绍了C++中的类模板,从定义到使用,深入探讨了模板类、非类型参数、模板的具体化、成员模板等方面,展示了模板的多功能性,如递归使用、使用多个类型参数以及默认类型模板参数。同时,还讨论了模板类和友元的关系,包括非模板友元、约束模板友元和非约束模板友元。最后提到了C++11引入的模板别名和可变参数模板等新特性。

1. 定义类模板

        程序清单类模板-1列出了类模板和成员函数模板,明确这些模板不是类和成员函数定义很重要,因为它们是C++编译指令,说明了如何生成类和成员函数定义。

        不能将模板成员函数放在独立的实现文件中,由于模板不是函数,它们不能单独编译,模板必须与特定的模板实例化请求一起使用。因此,最简单的方式是将所有模板信息放在一个头文件中,并再要使用这些模板的文件中包含该头文件。

//程序清单  类模板-1
//stacktp.h
#ifndef STACKTP_H_
#define STACKTP_H_

template<class Type>
class Stack
{
private:
    enum {MAX = 10};           //Stack类拥有的常量
    Type items[MAX];
    int top;

public:
    Stack();
    bool isempty();
    bool isfull();
    bool push(const Type& item);    //add item to stack
    bool pop(Type& item);           //pop top into item
};

template<class Type>
Stack<Type>::Stack()
{
    top = 0;
}

template<class Type>
bool Stack<Type>::isempty()
{
    return top == 0;
}

template<class Type>
bool Stack<Type>::isfull()
{
    return top == MAX;
}

template<class Type>
bool Stack<Type>::push(const Type& item)
{
    if(top < MAX)
    {
        items[top++] = item;
        return true;
    }
    else
        return false;
}

template<class Type>
bool Stack<Type>::pop(Type& item)
{
    if(top > 0)
    {
        item = items[--top];
        return true;
    }
    else
        return false;
}

#endif

注意模板成员函数定义与类模板声明一起放在模板的头文件中。

2. 使用模板类

        仅在程序中包含模板并不能生成模板类,必须请求实例化,必须显式地提供所需的类型。

Stack<int> kernels;
Stack<std::string> colonels;

3. 深入探讨模板类

        正确使用指针栈的方法之一是,让调用程序提供一个指针数组,其中每个指针都指向不同的字符串。注意,栈的任务是管理指针,不是创建指针。程序清单类模板-2重新定义了Stack<Type>类,使Stack构造函数能够接受一个可选大小的参数。这涉及到在内部使用动态数组,因此Stack类需要包含一个析构函数、一个复制构造函数和一个赋值运算符。

//程序清单 类模板-2
//stcktp1.h

#ifndef STCKTP1_H_
#define STCKTP1_H_

template<class Type>
class Stack
{
private:
    enum {SIZE = 10};  //默认大小
    int stacksize;
    Type* items;
    int top;

public:
    explicit Stack(int ss = SIZE);
    Stack(const Stack& st);
    ~Stack() { delete[] items; }
    bool isempty() { return top == 0; }
    bool isfull() { return top == stacksize; }
    bool push(const Type& item);
    bool pop(Type& item);
    Stack& operator=(const Stack& st);
};

template<class Type>
Stack<Type>::Stack(int ss): stacksize(ss), top(0)
{
    item = new Type[stacksize];
}

template<class Type>
Stack<Type>::Stack(const Stack<Type>& st)
{
    stacksize = st.stacksize;
    top = st.top;
    items = new Type[stacksize];
    for(int i = 0; i < top; i++)
    {
        items[i] = st.items[i];
    }
}

template<class Type>
bool Stack<Type>::push(const Type& item)
{
    if(top < stacksize)
    {
        items[top++] = item;
        return true;
    }
    return false;
}

template<class Type>
bool Stack<Type>::pop(Type& item)
{
    if(top > 0)
    {
        item = items[--top];
        return true;
    }
    return false;
}

template<class Type>
Stack<Type>& Stack<Type>::operator=(const Stack<Type>& st)
{
    if(this == &st)
        return *this;
    delete[] items;
    stacksize = st.stacksize;
    top = st.top;
    items = new Type[stacksize];
    for(int i = 0; i < top; i++)
    {
        items[i] = st.items[i];
    }
    return *this;
}

#endif

程序清单类模板-3使用了这个Stack模板类。

//程序清单 类模板-3
//stkoptr1.cpp

#include <iostream>
#include <cstdlib>
#include <ctime>
#include "stcktp1.h"
const int Num = 10;

int main()
{
    std::srand(std::time(0));
    std::cout << "Please enter stack size: ";
    int stacksize;
    std::cin >> stacksize;

    //create an empty stack with stacksize siots
    Stack<const char*> st(stacksize);

    //in basket
    const char* in[Num] = {
        " 1: Hank Gilgamesh", " 2: Kiki Ishtar",
        " 3: Betty Rocker", " 4: Ian Flagranti",
        " 5: Wolfgang Kibble", " 6: Portia Koop",
        " 7: Joy Almondo", " 8: Xaverie Paprika",
        " 9: Juan Moore", " 10: Misha Mache"
    };

    //out basket
    const char* out[Num];           //类型为const char*,因为指针数组被初始化为一组字符串常量

    int processed = 0;
    int nextin = 0;
    while(processed < Num)
    {
        if(st.isempty())
            st.push(in[nextin++]);
        else if(st.isfull())
            st.pop(out[processed++]);
        else if(std::rand() % 2 && nextin < Num)
            st.push(in[nextin++]);
        else
            st.pop(out[processed++]);
    }

    for(int i = 0; i < Num; i++)
    {
        std::cout << out[i] << std::endl;
    }
    std::cout << "Bye\n";

    return 0;
}

4. 数组模板示例和非类型参数

        模板常用作容器类。

        自己设计一个允许指定数组大小的简单数组模板。一种方法是在类中使用动态数组和构造函数参数来提供元素数目,例如程序清单类模板-2;另一种方法是使用模板参数来提供常规数组的大小,C++11新增的模板array就是这样做的,程序清单类模板-4演示了如何做。

//程序清单 类模板-4
//arraytp.h

#ifndef ARRAYTP_H_
#define ARRAYTP_H_

#include <iostream>
#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 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)
    {
        std::cerr << "Error in array limits: " << i << " is out of range\n";
        std::exit(EXIT_FAILURE);
    }
    return ar[i];
}

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

#endif

        关键字class指出T为类型参数,int指出n的类型为int。这种参数(指定特殊的类型而不是用作泛型名)称为非类型(non-type)或表达式(expression)参数。

        表达式参数有一些限制,表达式参数可以是整形、枚举、引用或指针。因此double m是不合法的,但double& rm和double* pm是合法的。另外,模板代码不能改变参数的值,也不能使用参数的地址。所以在ArrayTP模板中不能使用诸如n++和&n等表达式。另外,实例化模板时,用作表达式参数的值必须是常量表达式。

5. 模板的多功能性

        可以将用于常规类的技术用于模板类。模板类可以用作基类,也可用作组件类,还可用作其他模板的类型参数。例如,可以使用数组模板实现栈模板,也可以使用数组模板来构造数组——数组元素是基于栈模板的栈。即可以编写如下代码:

template<typename T>
class Array
{
private:
    T entry;
    //...
};

template<typename Type>
class GrowArray: public Array<Type>
{
    //...               //inheritance
}

template<typename Tp>
class Stack
{
    Array<Tp> ar;       //use an Array<> as a component
}

//...
Array< Stack<int> > assi;      // an array of stacks of int

5.1 递归使用模板

        可以递归使用模板,例如对于前面的数组模板定义,可以这样使用它:

ArrayTP< ArrayTP<int, 5>, 10> twodee;

        与之等价的常规数组声明如下:

int twodee[10][5]

5.2 使用多个类型参数

        模板可以包含多个类型参数。例如,假设希望类可以保存两种值,则可以创建并使用Pair模板来保存两个不同的值(标准模板库提供了类似的模板,名为pair)。程序清单类模板-5所示的小程序是一个这样的示例。其中,方法first() const和second() const报告存储的值。由于这两个方法返回Pair数据成员的引用,因此可以通过赋值重新设置存储的值。

//程序清单  类模板-5
//pairs.cpp -- defining and using a Pair template
#include <iostream>
#include <string>
template<class T1, class T2>
class Pair
{
private:
    T1 a;
    T2 b;
public:
    T1& first();
    T2& second();
    T1 first() const { return a; }
    T2 second() const { return b; }
    Pair(const T1& aval, const T2& bval): a(aval), b(bval) {}
    Pair() {}
};

template<class T1, class T2>
T1& Pair<T1, T2>::first()
{
    return a;
}

template<class T1, class T2>
T2& Pair<T1, T2>::second()
{
    return b;
}

int main()
{
    using std::cout;
    using std::endl;
    using std::string;
    Pair<string, int> ratings[4] = 
    {
        Pair<string, int>("The Purpled Duck", 5),
        Pair<string, int>("Jaquie's Frisco Al Fresco", 4),
        Pair<string, int>("Cafe Souffle", 5),
        Pair<string, int>("Bertie's Eats", 3)
    };

    int joints = sizeof(ratings) / sizeof(Pair<string, int>);  //必须用Pair<string, int>来调用构造函数,并将它作为sizeof的参数,因为类名是Pair<string, int>,而不是Pair
    cout << "Rating:\t Batery\n";
    for(int i = 0; i < joins; i++)
        cout << ratings[i].second() << ":\t"
        << ratings[i].first() << endl;
    cout << "Oops! Revised rating:\n";
    ratings[3].first() = "Bertie's Fab Eats";
    ratings[3].second() = 6;
    cout << rating[3].second() << ":\t "
        << ratings[3].first() << endl;

    return 0;
}

5.3 默认类型模板参数

        类模板的另一项新特性是,可以为类型参数提供默认值:

template<class T1, class T2 = int>
class Topo
{
//...
};

        这样,如果省略了T2的值,编译器将使用int。标准模板库经常使用该特性,将默认类型设置为类。虽然可以为类模板类型参数提供默认值,但不能为函数模板参数提供默认值。然而,可以为非类型参数提供默认值,这对于类模板和函数模板都是适用的。

6. 模板的具体化

        类模板与函数模板很相似,因为可以有隐式实例化、显式实例化和显式具体化。它们统称为具体化(specialization)。模板以泛型的方式描述类,而具体化是使用具体的类型生成类声明

6.1 隐式实例化

        目前为止,本文章所有的模板示例使用的都是隐式实例化,即它们声明一个或多个对象,指出所需的类型,而编译器使用通用模板提供的说明生成具体的类定义。

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

        编译器在需要对象之前,不会生成类的隐式实例化

ArrayTP<double, 30> *pt;   //仅声明一个指针,不需要实际对象
pt = new ArrayTP<double, 30>;   //现在需要一个实际对象

        第二条语句导致编译器生成类定义,并根据该定义创建一个对象。

6.2 显式实例化

        当使用关键字template并指出所需类型来声明类时,编译器将生成类声明的显式实例化。声明必须位于模板定义所在的名称空间中。

template class ArrayTP<string, 100>;

        此时,虽然没有创建类对象,编译器也将生成类声明(包括方法定义)。和隐式实例化一样,也将根据通用模板来生成具体化。

6.3 显式具体化

        显式具体化是特定类型(用于替换模板中的泛型)的定义。有时候,可能需要在为特殊类型实例化时,对模板进行修改,使其行为不同。此时可以创建显式具体化。例如,假设已经为用于表示排序后数组的类(元素在加入时被排序)定义了一个模板:

template <typename T>
class SortedArray
{
    //details omitted
};

        另外,假设模板使用>运算符来对值进行比较。对于数字,这样管用。如果T表示一种类,则只要定义了T::operator>()方法,这也管用。但如果T是const char*字符串,将不管用。如果强行使用此模板,则字符串将按地址(按照字母顺序)排序。这要求类定义使用strcmp(),而不是>来对值进行比较。此时可以提供一个显式模板具体化,这将采用为具体类型定义的模板,而不是为泛型定义的模板。当具体化模板和通用模板都与实例化请求匹配时,编译器将使用具体化版本。

        具体化类模板定义的格式:

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

         其中的实现代码将使用strcmp()(而不是>)来比较数组值。现在,当请求const char*类型的SortedArray模板时,编译器将使用上述专用的定义,而不是通用的模板定义。

6.4 部分具体化

        C++还允许部分具体化(partial specialization),即部分限制模板的通用性。

//模板声明
template<class T1, class T2> class Pair
{
};

//部分具体化:将T2限制为int
template<class T1> class Pair<T1, int>
{
};

        注意,如果指定所有的类型,则<>内将为空,这将导致显示具体化:

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

        也可以通过为指针提供特殊版本来部分具体化现有的模板:

template<class T>
class Feeb
{
};

template<class T*>
class Feeb
{
};

7. 成员模板

        模板可用作结构、类或模板类的成员。要完全实现STL的设计,必须使用这项特性。

// 程序清单 类模板-6
// 该程序清单提供一个简单的模板类示例,该模板类将另一个模板类和模板函数作为其成员
// tempmemb.cpp -- template members

#include <iostream>
using std::cout;
using std::endl;
template<typename T>
class beta
{
private:
    template<typename V>
    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>
    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);
    cout << "T was set to double\n";
    guy.Show();
    cout << "V was set to T, which is double, then V was set to int\n";
    cout << guy.blab(10, 2.3) << endl;
    cout << "U was set to int\n";
    cout << guy.blab(10.0, 2.3) << endl;
    cout << "U was set to double\n";
    cout << "Done\n";
    
    return 0;
}

        注意:blab()方法的U类型由该方法被调用时的参数值显式确定,T类型由对象的实例化类型确定。在这个例子中,guy的声明将T的类型设置为double,而下述方法调用的第一个参数将U的类型设置为int(参数10对于的类型):

cout << guy.blab(10, 2.5) << endl;

        因此,虽然混合类型引起的自动类型转换导致blab()中的计算以double类型进行,但返回值的类型为U(即int),因此它被截断为28。

        可以在beta模板中声明hold类和blab方法,并在beta模板的外面定义它们。如果所用的编译器接受类外面的定义,则在beta模板之外定义模板方法的代码如下:

template<typename T>
class beta
{
private:
    template<typename V> class hold;
    hold<T> q;
    hold<int> n;

public:
    beta(T t, int i): q(t), n(i) {}
    template<typename U>
    U blab(U u, T t);
    void Show() const { q.show(); n.show(); }
};

//成员定义
template<typename T>
    template<typename V>
        class beta<T>::hold
        {
        private:
            V val;
        public:
            hold(V v = 0): val(v) {}
            void show() const { std::cout << val << std::endl(); }
            V Value() const { return val; }
        };
template<typename T>
    template<typename U>
        U beta<T>::blab(U u, T t)
        {
            return (n.Value() + q.Value()) * u / t; 
        }

        因为模板是嵌套的,必须使用下面的语法:

template<typename T>
    template<typename V>

        定义还必须指出hold和blab是beta<T>类的成员。这是通过使用作用域解析运算符来完成的。

8. 将模板用作参数

        模板可以包含类型参数(如typename T)和非类型参数(如int n)。模板还可以包含本身就是模板的参数,这种参数是模板新增的特性,用于实现STL。

// 程序清单 - 类模板-7  将模板用作参数
// tempparm.cpp - templates as parameters

#include <iostream>
#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()
{
    using std::cout;
    using std::cin;
    using std::endl;
    Crab<Stack> nebula;
    // Stack class must match template<typename T> class Thing
    int ni;
    double nb;
    cout << "Enter int double pairs, such as 4 3.5 (0 0 to end):\n";
    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;
}

        可以混合使用模板参数和常规参数,例如,Crab类的声明可以像下面这样打头:

template<template<typename T> class Thing, typename U, typename V>
class Crab
{    
private:
    Thing<U> s1;
    Thing<V> s2;
    // ...
};

        现在,成员s1和s2可存储的数据类型为泛型,而不是用硬编码指定的类型。

9. 模板类和友元

        模板类声明也可以有友元。模板的友元分为3类:

  • 非模板友元;
  • 约束(bound)模板友元,即友元的类型取决于类被实例化时的类型;
  • 非约束(unbound)模板友元,即友元的所有具体化都是类的每一个具体化的友元。

9.1 模板类的非模板友元函数

        在模板类中将一个常规函数声明为友元:

template<class T>
class HasFriend
{
public:
    friend void counts();
};

        上述声明使counts()函数成为模板所有实例化的友元。例如,它将是类hasFriend<int>和hasFriend<string>的友元。

        counts()函数不是通过对象调用的(它是友元,不是成员函数),也没有对象参数,那么它如何访问HasFriend对象呢?有很多种可能性。它可以访问全局对象;可以使用全局指针访问非全局对象;可以创建自己的对象;可以访问独立于对象的模板类的静态数据成员。

        假设要为友元函数提供模板类参数,可以如下所示来进行友元声明吗?

friend void report(HasFriend&);   //impossible

        答案是不可以,原因是不存在HasFriend这样的对象,而只有特定的具体化,如HasFriend<short>。要提供模板类参数,必须指明具体化。例如:

template<class T>
class HasFriend
{
    friend void report(HasFriend<T>&);    //bound template friend
}

        上述代码示例,带HasFriend<int>参数的report()将成为HasFriend<int>类的友元。同样,带HasFriend<double>参数的report()将是report()的一个重载版本——它是HasFriend<double>类的友元。注意,report()本身并不是模板函数,而只是使用一个模板作参数。这意味着必须为要使用的友元定义显式具体化:

void report(HasFriend<short>&) { };
void report(HasFriend<int>&) { };
//程序清单 - 类模板-8 非模板友元
//frnd2tmp.cpp -- template class with non-template friends

#include <iostream>
using std::cout;
using std::endl;

template<typename T>
class HasFriend
{
private:
    T item;
    static int ct;

public:
    HasFriend(const T& i): item(i) { ct++; }
    ~HasFriend() { ct--; }
    friend void counts();
    friend void reports(HasFriend<T>&);   //模板作参数
};

// each specialization has its own static data member
template<typename T>
int HasFriend<T>::ct = 0;
// non template friend to all HasFriend<T> classes
void counts()
{
    cout << "int count: " << HasFriend<int>::ct << "; ";
    cout << "double count: " << HasFriend<double>::ct << endl;
}
// non template friend to the HasFriend<int> class
void report(HasFriend<int>& hf)
{
    cout << "HasFriend<int>: " << hf.item << endl;
}
// non template friend to the HasFriend<double> class
void reports(HasFriend<double>& hf)
{
    cout << "HasFriend<double>: " << hf.item << endl;
}

int main()
{
    // ...
}

        程序清单类模板-8中,HasFriend模板有一个静态成员ct。这意味着这个类的每一个特定的具体化都将有自己的静态成员。count()方法是所有HasFriend具体化的友元,它报告两个特定的具体化(HasFriend<int>和HasFriend<double>)的ct的值。该程序还提供两个report()函数,它们分别是某个特定HasFriend具体化的友元。

9.2 模板类的约束模板友元函数

        可以修改前一个示例,使友元函数本身成为模板。具体地说,为约束模板友元作准备,要使类的每一个具体化都获得与友元匹配的具体化。这比非模板友元复杂些,包含以下三步:

        首先,在类定义的前面声明每个模板函数。

template <typename T> void counts();
template <typename T> void report(T&);

        然后,在模板类中再次将模板函数声明为友元。这些语句根据类模板参数的类型声明具体化:

template <typename TT>
class HasFriendT
{
    //...
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT>&);
};

        声明中的<>指出这是模板具体化。对于report(),<>可以为空,因为可以从函数参数推断出如下模板类型参数:HasFriend<TT>。也可以使用:

report<HasFriendT<TT> >(HasFriendT<TT>&);

        但counts()函数没有参数,因此必须使用模板参数语法(<TT>)来指明具体化。需要注意,TT是HasFriendT类的参数类型。

        理解这些声明的最佳方式是设想声明一个特定具体化的对象时,它们将变成什么样。例如,假设声明了这样一个对象:

HasFriendT<int> squack;        

        编译器将用int替换TT,并生成下面的类定义:

class HasFriendT<int>
{
//...
    friend void counts<int>();
    friend void report<>(HasFriendT<int>&);
};

        程序清单类模板-9说明了上述知识点,注意类模板-8包含1个counts()函数,它是所有HasFriend类的友元;而类模板-9包含2个counts()函数,它们分别是某个被实例化的类类型的友元。因为counts()函数调用没有可被编译器用来推断出所需具体化的函数参数,所以这些调用使用counts<int>和counts<double>指明具体化。但对于report()调用,编译器可以从参数类型推断出要使用的具体化。使用<>格式也能获得同样的效果:

report<HasFriendT<int> >(hfi2);
// 类模板-9
// tmp2tmp.cpp -- template friends to a template class

#include <iostream>
using std::cout;
using std::endl;

//template prototypes
template<typename T> void counts();
template<typename T> void report(T&);

//template class
template<typename TT>
class HasFriendT
{
private:
    TT item;
    static int ct;
public:
    HasFriendT(const TT& i): item(i) { ct++; }
    ~HasFriendT() { ct--; }
    friend void counts<TT>();
    friend void report<>(HasFriendT<TT>&);
};

template<typename T>
int HasFriendT<T>::ct = 0;

//template friend functions definitions
template<typename T>
void counts()
{
    cout << "template size: " << sizeof(HasFriendT<T>) << "; ";
    cout << "template counts(): " << HasFriendT<T>::ct << endl;
}

template<typename T>
void report(T& hf)
{
    cout << hf.item << endl;
}

int main()
{
    counts<int>();
    HasFriendT<int> hfi1(10);
    HasFriendT<int> hfi2(20);
    HasFriend<double> hfdb(10.5);
    report(hfi1);   //generate report(HasFriendT<int>&)
    report(hfi2);
    report(hfdb);
    cout << "counts<int>() output:\n";
    counts<int>();
    cout << "counts<double>() output:\n";
    counts<double>();

    return 0;
}

9.3 模板类的非约束模板友元函数

        前一节中的约束模板友元函数是在类外面声明的模板的具体化。int类具体化获得int函数具体化,以此类推。通过在类内部声明模板,可以创建非约束友元函数,即每个函数具体化都是每个类具体化的友元。对于非约束友元,友元模板类型参数与模板类类型参数是不同的:

template<typename T>
class ManyFriend
{
    template<typename C, template D> friend void show2(C&, D&);
};

        程序清单类模板-10是一个使用非约束友元的例子。其中,函数调用show2(hfi1, hfi2)与下面的具体化匹配:

void show2<ManyFriend<int>&, ManyFriend<int>& > 
            (ManyFriend<int>& c, ManyFriend<int>& d);

        因为它是所有ManyFriend具体化的友元,所以能够访问所有具体化的item成员,但它只访问了ManyFriend<int>对象。

        同样,show2(hfi1, hfi2)与下面具体化匹配:

void show2<ManyFriend<double>&, ManyFriend<int>& >
            (ManyFriend<double>& c, ManyFriend<int>& d);

        它也是所有ManyFriend具体化的友元,并访问了ManyFriend<int>对象的item成员和ManyFriend<double>对象的item成员。

// 类模板-10
// manyfrnd.cpp -- unbound template friend to a template class

#include <iostream>
using std::cout;
using std::endl;

template<typename T>
class ManyFriend
{
private:
    T item;
public:
    ManyFriend(const T& i): item(i) {}
    template <typename C, typename D> friend void show2(C&, D&);
};

template <typename C, typename D> void show2(C& c, D& d)
{
    cout << c.item << ", " << d.item <<  endl;
}

int main()
{
    ManyFriend<int> hfi1(10);
    ManyFriend<int> hfi2(20);
    ManyFriend<double> hfdb(10.5);
    cout << "hfi1, hfi2: ";
    show2(hfi1, hfi2);
    cout << "hfdb, fhi2: ";
    show2(hfdb, hfi2);

    return 0;
}

10. 模板别名(C++11)

        如果能为类型指定别名,将很方便,在模板设计中尤其如此。可使用typedef为模板具体化指定别名:

// define 3 typedef aliases
typedef std::array<double, 12> arrd;
typedef std::array<int, 12> arri;
typedef std::array<std::string, 12> arrst;
arrd gallons;
arri days;
arrst months;

        C++11新增了一项功能——使用模板提供一系列别名,如下所示:

typename<typename T>
 using arrtype = std::array<T, 12>   // template to create multiple aliases

        这将arrtype定义为一个模板别面,可使用它来指定类型,如下所示:

 arrtype<double> gallons;
 arrtype<int> days;
 arrtype<std::string> months;

        C++11允许将语法using=用于非模板。用于非模板时,这种语法与常规typedef等价:

 typedef const char* pc1;
 using pc2 = const char*;       //using = syntax
 typedef const int *(*pa1)[10];   //typedef syntax
 using pa2 = const int*(*)[10];   //using = syntax

        这种语法的可读性更强,它让类型名和类型信息更清晰。

        C++11新增的另一项模板功能是可变参数模板(variadic template),让您能够定义这样的模板类和模板函数,即可接受可变数量的参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值