非类型模板参数(Nontype Template Parameters)


对于函数模板和类模板,模板参数不一定必须是类型,也可是是常规的数值。当以类型(type)作为模板参数的时候,代码中未决定的是类型;当以一般的数字(non-type)作为模板参数的时候,代码中待定的内容便是某些数值。使用者这种模板必须要显示指定数值,模板才能实例化。

1、非类型类模板参数(Nontype Class Template Parameters)

之前章节中的列子中Stack类中使用vector或deque来存储元素。我们也可以使用一个固定大小的数值来存储元素。这么做的好处是在定义一个Stack对象的时候就分配了固定大小的内存空间,之后的元素操作就不再需要内存分配管理了。坏处是这个数值固定大小的设置比较困难,如果设置太小很数组容易满;如果设置太大又浪费内存空间。
一个可行方法是让使用者自己定义数组的最大空间。
如下:

// basics/stack4.hpp 

#include <stdexcept> 

template <typename T, int MAXSIZE> 
class Stack { 
  private: 
    T elems[MAXSIZE];        // elements 
    int numElems;            // current number of elements 
  public: 
    Stack();                 // constructor 
    void push(T const&);     // push element 
    void pop();              // pop element 
    T top() const;           // return top element 
    bool empty() const {     // return whether the stack is empty 
        return numElems == 0; 
    } 
    bool full() const {      // return whether the stack is full 
        return numElems == MAXSIZE; 
    } 
}; 

// constructor 
template <typename T, int MAXSIZE> 
Stack<T,MAXSIZE>::Stack () 
  : numElems(0)              // start with no elements 
{ 
    // nothing else to do 
}

template <typename T, int MAXSIZE> 
void Stack<T,MAXSIZE>::push (T const& elem) 
{ 
    if (numElems == MAXSIZE) { 
        throw std::out_of_range("Stack<>::push(): stack is full"); 
    } 
    elems[numElems] = elem;  // append element 
    ++numElems;              // increment number of elements 
} 

template<typename T, int MAXSIZE> 
void Stack<T,MAXSIZE>::pop () 
{ 
    if (numElems <= 0) { 
        throw std::out_of_range("Stack<>::pop(): empty stack"); 
    } 
    --numElems;              // decrement number of elements 
} 

template <typename T, int MAXSIZE> 
T Stack<T,MAXSIZE>::top () const 
{ 
    if (numElems <= 0) { 
        throw std::out_of_range("Stack<>::top(): empty stack"); 
    } 
    return elems[numElems-1];  // return last element 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

第二个新的模板参数MAXSIZE是int类型,它来指定stack对象所能容纳的最大元素个数。

使用这个Statck类模板的时候,需要指定元素类型和所能容纳的最大元素个数。如下:

Stack<int,20>         int20Stack;    // stack of up to 20 ints 
Stack<int,40>         int40Stack;    // stack of up to 40 ints 
Stack<std::string,40> stringStack;   // stack of up to 40 strings 
// manipulate stack of up to 20 ints 
int20Stack.push(7); 
int20Stack.pop(); 

// manipulate stack of up to 40 strings 
stringStack.push("hello"); 
std::cout << stringStack.top() << std::endl; 
stringStack.pop(); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

每一个模板的实例都有自己的类型。上述的例子中,“int20Stack”和“int40Stack”是两个不同的类型,因此这两个类型之间是不能相互隐式或显示类型转换,也不能相互替换,也不能相互赋值。
可以对模板参数设置默认值,如下:

template <typename T = int, int MAXSIZE = 100> 
class Stack { 
  … 
};
  • 1
  • 2
  • 3
  • 4

2、非类型函数模板参数(Nontype Function Template Parameters)

可以为函数模板定义非类型参数,如下:

template <typename T, int VAL> 
T addValue (T const& x) 
{ 
    return x + VAL; 
} 
  • 1
  • 2
  • 3
  • 4
  • 5

3、非类型模板参数的局限(Restrictions for Nontype Template Parameters)

非类型模板有它的局限。通常它们只能是常数整数(constant integral values )包括枚举,或者是指向外部链接的指针。

float或者类类型的对象是不被允许的,如下:

template <double VAT>        // ERROR: floating-point values are not 
double process (double v)    //        allowed as template parameters 
{ 
    return v * VAT; 
} 

template <std::string name>  // ERROR: class-type objects are not 
class MyClass {              //        allowed as template parameters 
  … 
}; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

字符串常量不能作为模板参数,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

MyClass<"hello"> x;   // ERROR: string literal "hello" not allowed 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

全局指针也不能作为非类型的模板参数,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

char const* s = "hello"; 

MyClass<s> x;         // ERROR: s is pointer to object with internal linkage 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

但是如下代码是可以的:

template <char const* name> 
class MyClass { 
  … 
}; 

extern char const s[] = "hello"; 

MyClass<s> x;        // OK 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

因为全局的char类型的数组已经被初始化为了”hello”,这是一个外部链接的对象。

4、总结

1、模板参数不仅仅可以是类型(type),还可以是值(value)
2、不能把float,class-type类型的对象,内部链接(internal linkage )对象,作为非类型模板参数。

对于函数模板和类模板,模板参数不一定必须是类型,也可是是常规的数值。当以类型(type)作为模板参数的时候,代码中未决定的是类型;当以一般的数字(non-type)作为模板参数的时候,代码中待定的内容便是某些数值。使用者这种模板必须要显示指定数值,模板才能实例化。

1、非类型类模板参数(Nontype Class Template Parameters)

之前章节中的列子中Stack类中使用vector或deque来存储元素。我们也可以使用一个固定大小的数值来存储元素。这么做的好处是在定义一个Stack对象的时候就分配了固定大小的内存空间,之后的元素操作就不再需要内存分配管理了。坏处是这个数值固定大小的设置比较困难,如果设置太小很数组容易满;如果设置太大又浪费内存空间。
一个可行方法是让使用者自己定义数组的最大空间。
如下:

// basics/stack4.hpp 

#include <stdexcept> 

template <typename T, int MAXSIZE> 
class Stack { 
  private: 
    T elems[MAXSIZE];        // elements 
    int numElems;            // current number of elements 
  public: 
    Stack();                 // constructor 
    void push(T const&);     // push element 
    void pop();              // pop element 
    T top() const;           // return top element 
    bool empty() const {     // return whether the stack is empty 
        return numElems == 0; 
    } 
    bool full() const {      // return whether the stack is full 
        return numElems == MAXSIZE; 
    } 
}; 

// constructor 
template <typename T, int MAXSIZE> 
Stack<T,MAXSIZE>::Stack () 
  : numElems(0)              // start with no elements 
{ 
    // nothing else to do 
}

template <typename T, int MAXSIZE> 
void Stack<T,MAXSIZE>::push (T const& elem) 
{ 
    if (numElems == MAXSIZE) { 
        throw std::out_of_range("Stack<>::push(): stack is full"); 
    } 
    elems[numElems] = elem;  // append element 
    ++numElems;              // increment number of elements 
} 

template<typename T, int MAXSIZE> 
void Stack<T,MAXSIZE>::pop () 
{ 
    if (numElems <= 0) { 
        throw std::out_of_range("Stack<>::pop(): empty stack"); 
    } 
    --numElems;              // decrement number of elements 
} 

template <typename T, int MAXSIZE> 
T Stack<T,MAXSIZE>::top () const 
{ 
    if (numElems <= 0) { 
        throw std::out_of_range("Stack<>::top(): empty stack"); 
    } 
    return elems[numElems-1];  // return last element 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

第二个新的模板参数MAXSIZE是int类型,它来指定stack对象所能容纳的最大元素个数。

使用这个Statck类模板的时候,需要指定元素类型和所能容纳的最大元素个数。如下:

Stack<int,20>         int20Stack;    // stack of up to 20 ints 
Stack<int,40>         int40Stack;    // stack of up to 40 ints 
Stack<std::string,40> stringStack;   // stack of up to 40 strings 
// manipulate stack of up to 20 ints 
int20Stack.push(7); 
int20Stack.pop(); 

// manipulate stack of up to 40 strings 
stringStack.push("hello"); 
std::cout << stringStack.top() << std::endl; 
stringStack.pop(); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

每一个模板的实例都有自己的类型。上述的例子中,“int20Stack”和“int40Stack”是两个不同的类型,因此这两个类型之间是不能相互隐式或显示类型转换,也不能相互替换,也不能相互赋值。
可以对模板参数设置默认值,如下:

template <typename T = int, int MAXSIZE = 100> 
class Stack { 
  … 
};
  • 1
  • 2
  • 3
  • 4

2、非类型函数模板参数(Nontype Function Template Parameters)

可以为函数模板定义非类型参数,如下:

template <typename T, int VAL> 
T addValue (T const& x) 
{ 
    return x + VAL; 
} 
  • 1
  • 2
  • 3
  • 4
  • 5

3、非类型模板参数的局限(Restrictions for Nontype Template Parameters)

非类型模板有它的局限。通常它们只能是常数整数(constant integral values )包括枚举,或者是指向外部链接的指针。

float或者类类型的对象是不被允许的,如下:

template <double VAT>        // ERROR: floating-point values are not 
double process (double v)    //        allowed as template parameters 
{ 
    return v * VAT; 
} 

template <std::string name>  // ERROR: class-type objects are not 
class MyClass {              //        allowed as template parameters 
  … 
}; 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

字符串常量不能作为模板参数,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

MyClass<"hello"> x;   // ERROR: string literal "hello" not allowed 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

全局指针也不能作为非类型的模板参数,如下:

template <char const* name> 
class MyClass { 
  … 
}; 

char const* s = "hello"; 

MyClass<s> x;         // ERROR: s is pointer to object with internal linkage 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

但是如下代码是可以的:

template <char const* name> 
class MyClass { 
  … 
}; 

extern char const s[] = "hello"; 

MyClass<s> x;        // OK 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

因为全局的char类型的数组已经被初始化为了”hello”,这是一个外部链接的对象。

4、总结

1、模板参数不仅仅可以是类型(type),还可以是值(value)
2、不能把float,class-type类型的对象,内部链接(internal linkage )对象,作为非类型模板参数。

猜你喜欢

转载自blog.csdn.net/windgs_yf/article/details/80915645