首页 > 其他 > 详细

template(3.1)

时间:2014-05-10 23:09:53      阅读:411      评论:0      收藏:0      [点我收藏+]

Nontype Template Parameters非类型模板参数

对 function templates 和 class templates 而言,template parameters 并不一定非要是类型(types) 不可,它们也可以是常规的(一般的)数值。当你以类型(types)作为 template parameters时, 程序代码中尚未决定的是类型;当你以一般数值(non-types)作为 template parameter 时,程序代码中待定的内容便是某些数值。使用这种template时必须明确指定数值,程序代码才得以实例化。 本章将利用这个特性实作一个新版本的stack class template。此外我将举一个例子,展示如何在 function templates 中使用 nontype template paramaters,并讨论此技术的一些局限。

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

上一章实作了一个「元素个数可变」的stack class。与之对比,你也可以实作另一种stack,透过一个固定大小(fixed-size)的 array来容纳元素。这样做的好处是不必考虑诸如内存管理之类的问题。然而array大小的决定是一件比较困难的事:array愈小则stack愈容易满溢,array愈大则愈容易造成空间浪费。一个可行的解决办法是让使用者指定array大小,这个大小也就是stack 的最大元素个数。为了完成以上想法,我们应该把大小值当作一个 template parameter:

bubuko.com,布布扣
/* The following code example is taken from the book
 * "C++ Templates - The Complete Guide"
 * by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
 *
 * (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
#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
}
bubuko.com,布布扣

新加入的第二个 template parameter MAXSIZE 隶属 int 类型,用来指定「容纳 stack 元素」的那个底部 array 的大小:

bubuko.com,布布扣
template <typename T, int MAXSIZE>
class Stack {
private:
T elems[MAXSIZE]; // 元素
...
};
push()便是使用 MAXSIZE 来检查 stack 是否已满:
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; // 追加
++numElems; // 元素总数加 1
}
bubuko.com,布布扣

使用上述 class template 时,必须同时指定 (1) 元素类型和 (2) stack 元素的最大数量:

bubuko.com,布布扣
/* The following code example is taken from the book
 * "C++ Templates - The Complete Guide"
 * by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
 *
 * (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
#include <iostream>
#include <string>
#include <cstdlib>
#include "stack4.hpp"

int main()
{
    try {
        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);
        std::cout << int20Stack.top() << std::endl;
        int20Stack.pop();

        // manipulate stack of up to 40 strings
        stringStack.push("hello");
        std::cout << stringStack.top() << std::endl; 
        stringStack.pop();
        stringStack.pop();
    }
    catch (std::exception const& ex) {
        std::cerr << "Exception: " << ex.what() << std::endl;
        return EXIT_FAILURE;  // exit program with ERROR status
    }
}
bubuko.com,布布扣
注意,每一个被实例化(instantiated)的 class template 都有各自的类型。(译注:常见的误会是:上述三个 stacks 隶属同一类型。这是错误观念。)因此 int20Stack 和 int40Stack 是两个不同类型,不能互相进行隐式或显式转换,两者不能换用(彼此取代),也不能互相赋值。
你可以指定 non-type template parameters 的默认值:
bubuko.com,布布扣
template <typename T = int, int MAXSIZE = 100>
class Stack {
...
};
bubuko.com,布布扣

然而从设计角度来看,这样做并不恰当。Template parameters 的默认值应该符合大多数情况下的要求,然而把 int 当做预设元素类型,或指定 stack 最多有 100 个元素,并不符合一个「通用型 stack」的需求。更好的作法是让使用者指定这两个参数的值,并在文件中说明它们的意义。


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


你也可以为 function template 定义 nontype parameters。例如下面的 function template 定义了一组 函数,可以将参数 x 累加一个值(VAL)后传回:

// basics/addval.hpp


bubuko.com,布布扣
/* The following code example is taken from the book
 * "C++ Templates - The Complete Guide"
 * by David Vandevoorde and Nicolai M. Josuttis, Addison-Wesley, 2002
 *
 * (C) Copyright David Vandevoorde and Nicolai M. Josuttis 2002.
 * Permission to copy, use, modify, sell and distribute this software
 * is granted provided this copyright notice appears in all copies.
 * This software is provided "as is" without express or implied
 * warranty, and with no claim as to its suitability for any purpose.
 */
template <typename T, int VAL>
T addValue (T const& x)
{
    return x + VAL;
}
bubuko.com,布布扣
当我们需要把「函数」或「某种通用操作」作为参数传递时,这一类函数就很有用。例如使用STL(Standard Template Library,标准模板库)时,你可以运用上述 function template 的实例(instantiation),将某值加到元素集内的每一个元素身上:

bubuko.com,布布扣
// (1)
std::transform (source.begin(), source.end(), // 来源端起止位置
dest.begin(), // 目的端起始位置
addValue<int,5>); // 实际操作
bubuko.com,布布扣
c++官网上的transform示例:
bubuko.com,布布扣
// transform algorithm example
#include <iostream>     // std::cout
#include <algorithm>    // std::transform
#include <vector>       // std::vector
#include <functional>   // std::plus
int op_increase (int i) { return ++i; }
int main () {
std::vector
<int> foo; std::vector<int> bar; // set some values: for (int i=1; i<6; i++) foo.push_back (i*10); // foo: 10 20 30 40 50 bar.resize(foo.size()); // allocate space std::transform (foo.begin(), foo.end(), bar.begin(), op_increase); // bar: 11 21 31 41 51 // std::plus adds together its two arguments: std::transform (foo.begin(), foo.end(), bar.begin(), foo.begin(), std::plus<int>()); // foo: 21 41 61 81 101 std::cout << "foo contains:"; for (std::vector<int>::iterator it=foo.begin(); it!=foo.end(); ++it) std::cout << << *it; std::cout << \n; return 0; }
bubuko.com,布布扣
最后一个自变量将 function template addValue()实例化了,使其操作成为「将5加进一个int数值中」。算法transform()会对source 中的所有元素调用这个具现体(函数),然后把结果传入dest中。注意上述例子带来的一个问题:addValue<int,5> 是个 function template 实体(instance),而我们知道,所谓「function templates 实体」被认为是命名了一组重载函数集,即使该函数集内可能只有一个函数。根据目前标准,编译器无法借助「重载函数集」来进行 template parameter的推导。因此你不得不把 function template argument 强制转型为精确类型:

bubuko.com,布布扣
// (2)
std::transform (source.begin(), source.end(), // 来源端起止位置
dest.begin(), // 目的端起始位置
(int(*)(int const*)) addValue<int,5>); // 操作
C++ Standard 中 已 有一 个提案 要 求修正这种行为 ,使你不必 在这种场合强制 转型(请参 考 [CoreIssue115])。在尚未获得修正之前,为保证程序的可移植性,你还是得像上面那么做。
bubuko.com,布布扣
 

template(3.1),布布扣,bubuko.com

template(3.1)

原文:http://www.cnblogs.com/jianfengyun/p/3720710.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!