模板中的非类型参数

  模板是一种泛型编程技术,目的是将数据的类型参数化。模板中除了可以包含类型参数,还可以包含非类型参数。

template<typename T, int N> class Demo{ };
template<class T, int N> void func(T (&arr)[N]);

T是一个类型参数,N是一个非类型参数,用来传递数据的值,而不是类型,它和普通函数的参数一样,都需要指明具体的类型。

在函数模板中使用非类型参数

template<typename T> void Swap(T a[], T b[], int len);

此处传递函数参数时,数组长度len给参数的传递带来了不便,我们可以借助模板中的非类型参数将它消除。例如:

template<class T> void Swap(T &a, T &b);  //模板①:交换基本类型的值
template<typename T, unsigned N> void Swap(T (&a)[N], T (&b)[N]);  //模板②:交换两个数组
template<typename T, unsigned N> void printArray(T (&arr)[N]);  //打印数组元素
int main(){
    //交换基本类型的值
    int m = 10, n = 99;
    Swap(m, n);  //匹配模板①
    cout<<m<<", "<<n<<endl;
    //交换两个数组
    int a[5] = { 1, 2, 3, 4, 5 };
    int b[5] = { 10, 20, 30, 40, 50 };
    Swap(a, b);  //匹配模板②
    printArray(a);
    printArray(b);
    return 0;
}
template<class T> void Swap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}
template<typename T, unsigned N> void Swap(T (&a)[N], T (&b)[N]){
    T temp;
    for(int i=0; i<N; i++){
        temp = a[i];
        a[i] = b[i];
        b[i] = temp;
    }
}
template<typename T, unsigned N> void printArray(T (&arr)[N]){
    for(int i=0; i<N; i++){
        if(i == N-1){
            cout<<arr[i]<<endl;
        }else{
            cout<<arr[i]<<", ";
        }
    }
}

  编译器会使用数组类型int 来代替类型参数T,使用数组长度5来代替非类型参数N

在类模板中使用非类型参数

template<typename T, int N>
class Array{
public:
    Array();
    ~Array();
public:
    T & operator[](int i);  //重载下标运算符[]
    int length() const { return m_length; }  //获取数组长度
    bool capacity(int n);  //改变数组容量
private:
    int m_length;  //数组的当前长度
    int m_capacity;  //当前内存的容量(能容乃的元素的个数)
    T *m_p;  //指向数组内存的指针
};
template<typename T, int N>
Array<T, N>::Array(){
    m_p = new T[N];
    m_capacity = m_length = N;
}
template<typename T, int N>
Array<T, N>::~Array(){
    delete[] m_p;
}
template<typename T, int N>
T & Array<T, N>::operator[](int i){
    if(i<0 || i>=m_length){
        cout<<"Exception: Array index out of bounds!"<<endl;
    }
    return m_p[i];
}
template<typename T, int N>
bool Array<T, N>::capacity(int n){
    if(n > 0){  //增大数组
        int len = m_length + n;  //增大后的数组长度
        if(len <= m_capacity){  //现有内存足以容纳增大后的数组
            m_length = len;
            return true;
        }else{  //现有内存不能容纳增大后的数组
            T *pTemp = new T[m_length + 2 * n * sizeof(T)];  //增加的内存足以容纳 2*n 个元素
            if(pTemp == NULL){  //内存分配失败
                cout<<"Exception: Failed to allocate memory!"<<endl;
                return false;
            }else{  //内存分配成功
                memcpy( pTemp, m_p, m_length*sizeof(T) );
                delete[] m_p;
                m_p = pTemp;
                m_capacity = m_length = len;
            }
        }
    }else{  //收缩数组
        int len = m_length - abs(n);  //收缩后的数组长度
        if(len < 0){
            cout<<"Exception: Array length is too small!"<<endl;
            return false;
        }else{
            m_length = len;
            return true;
        }
    }
}
int main(){
    Array<int, 5> arr;
    //为数组元素赋值
    for(int i=0, len=arr.length(); i<len; i++){
        arr[i] = 2*i;
    }
   
    //第一次打印数组
    for(int i=0, len=arr.length(); i<len; i++){
        cout<<arr[i]<<" ";
    }
    cout<<endl;
   
    //扩大容量并为增加的元素赋值
    arr.capacity(8);
    for(int i=5, len=arr.length(); i<len; i++){
        arr[i] = 2*i;
    }
    //第二次打印数组
    for(int i=0, len=arr.length(); i<len; i++){
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    //收缩容量
    arr.capacity(-4);
    //第三次打印数组
    for(int i=0, len=arr.length(); i<len; i++){
        cout<<arr[i]<<" ";
    }
    cout<<endl;
    return 0;
}

  Array是一个类模板,它有一个类型参数T和一个非类型参数N,T指明了数组元素的类型,N指明了数组长度。

非类型参数的限制

1)当非类型参数是一个整数时,传递给它的实参,或者由编译器推导出的实参必须是一个常量表达式,例如:

int len;
cin>>len;
int a[len];
int b[len];
Swap(a, b);  //error
int len;
cin>>len;
Array<int, len> arr;  //error

这两种情况,编译器推导出来的实参是len,是一个变量,而不是一个常量,是错误的

2)当非类型参数是一个指针(引用)时,绑定到该指针的实参必须具有静态的生存期;换句话说,实参必须存储在虚拟地址空间的静态数据区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值