• template(3.1)


    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 << ' '; 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])。在尚未获得修正之前,为保证程序的可移植性,你还是得像上面那么做。
     4.3 Nontype Template Parameters 的局限
    注意,nontype template parameters 有某些局限:通常来说它们只能是常数整数(constant integral values),包括 enum,或是「指向外部链接(external linkage)之对象」的指针。以浮点数或 class-type objects 作为 nontype template parameters 是不可以的:
    
    
    template <double VAT> // 错误:浮点值不能作为 template parameters
    double process (double v)
    {
        return v * VAT;
    }
    
    template <std::string name> // 错误:class objects 不能作为 template parameters
    class MyClass {
        ...
    };
    
    
    不允许浮点字面常数(floating-point literals)或简单的常量浮点表达式(constant floating-point expressions)作为 template arguments,其实只是历史因素,并非技术原因。由于并没有什么实作上的困难,或许将来C++会支持它,请参考 13.4 节。由于字符串字面常数(string literal)是一种采用内部链接(internal linkage)的对象,也就是说不同模块(modules)内的两个同值的字符串字面常数,其实是不同的对象,因此它们也不能被拿来作为 template arguments:
    
    
    template <char const* name>
    class MyClass {
    ...
    };
    MyClass<"hello"> x; // 错误:不能使用字符串常量"hello"
    
    
    此外,全局指针也不能被拿来作为 template arguments:
    template <char const* name>
    Class MyClass {
    ...
    };
    char const* s = "hello";
    MyClass<s> x; // 错误:s 是「指向内部链接(internal linkage)对象」的指针
    但是你可以这么写:
    template <char const* name>
    Class MyClass {
    ...
    };
    extern char const s[] = "hello";
    MyClass<s> x; // OK
    全局的 char array s 被初始化为 "hello",因此 s 是一个外部链接(external linkage)对象。8.3.3节, 13.4 节则讨论了这个问题未来的可能变化。
    
    

    4.4 摘要

    Templates parameters 不限只能是类型(types),也可以是数值(values)。你不能把浮点数、class-type 对象、内部链接(internal linkage)对象(例如字符串字面常数) 当作 nontype template parameters 的自变量。
    
    
  • 相关阅读:
    人心散了、项目必然要败(转自CSDN)
    sql server加锁机制
    数据库事物隔离级别
    aop学习
    数据库加锁(转)
    托管代码和非托管代码效率的对比。
    day05 Linux文本处理命令
    day04 CentOS 异常,问题解决方法
    day02 Linux系统介绍与安装
    linux常用命令的英文单词缩写
  • 原文地址:https://www.cnblogs.com/jianfengyun/p/3720710.html
Copyright © 2020-2023  润新知