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:
/* 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 }
新加入的第二个 template parameter MAXSIZE 隶属 int 类型,用来指定「容纳 stack 元素」的那个底部 array 的大小:
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 }
使用上述 class template 时,必须同时指定 (1) 元素类型和 (2) stack 元素的最大数量:
/* 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 } }
注意,每一个被实例化(instantiated)的 class template 都有各自的类型。(译注:常见的误会是:上述三个 stacks 隶属同一类型。这是错误观念。)因此 int20Stack 和 int40Stack 是两个不同类型,不能互相进行隐式或显式转换,两者不能换用(彼此取代),也不能互相赋值。
你可以指定 non-type template parameters 的默认值:
template <typename T = int, int MAXSIZE = 100> class Stack { ... };
然而从设计角度来看,这样做并不恰当。Template parameters 的默认值应该符合大多数情况下的要求,然而把 int 当做预设元素类型,或指定 stack 最多有 100 个元素,并不符合一个「通用型 stack」的需求。更好的作法是让使用者指定这两个参数的值,并在文件中说明它们的意义。
4.2 Nontype Function Template Parameters(非类型函数模板参数)
你也可以为 function template 定义 nontype parameters。例如下面的 function template 定义了一组 函数,可以将参数 x 累加一个值(VAL)后传回:
// basics/addval.hpp
/* 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; }
当我们需要把「函数」或「某种通用操作」作为参数传递时,这一类函数就很有用。例如使用STL(Standard Template Library,标准模板库)时,你可以运用上述 function template 的实例(instantiation),将某值加到元素集内的每一个元素身上:
// (1) std::transform (source.begin(), source.end(), // 来源端起止位置 dest.begin(), // 目的端起始位置 addValue<int,5>); // 实际操作
c++官网上的transform示例:
// 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; }
最后一个自变量将 function template addValue()实例化了,使其操作成为「将5加进一个int数值中」。算法transform()会对source 中的所有元素调用这个具现体(函数),然后把结果传入dest中。注意上述例子带来的一个问题:addValue<int,5> 是个 function template 实体(instance),而我们知道,所谓「function templates 实体」被认为是命名了一组重载函数集,即使该函数集内可能只有一个函数。根据目前标准,编译器无法借助「重载函数集」来进行 template parameter的推导。因此你不得不把 function template argument 强制转型为精确类型:
// (2) std::transform (source.begin(), source.end(), // 来源端起止位置 dest.begin(), // 目的端起始位置 (int(*)(int const*)) addValue<int,5>); // 操作 C++ Standard 中 已 有一 个提案 要 求修正这种行为 ,使你不必 在这种场合强制 转型(请参 考 [CoreIssue115])。在尚未获得修正之前,为保证程序的可移植性,你还是得像上面那么做。
原文:http://www.cnblogs.com/jianfengyun/p/3720710.html