栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++模板编程(3)---模板编程基本技巧Tricky Basics

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++模板编程(3)---模板编程基本技巧Tricky Basics

  • 关键词typename的另一种用途
  • 将成员函数和嵌套类作为模板的模板模板参数template template paramters
  • 零值初始化zero Initialization
  • 字符串字面常量string literals作为模板参数

1. 关键词 typename

关键词typename是C++标准化过程中引入的,目的在向编译器说明template内的某个标识符是类型(而不是其他)。考虑如下例子:

template

class MyClass {

typename T::SubType * ptr;

...

};

在这里,第二个typename关键词的意思是SubType是class T内部定义的一个类型,从而ptr是一个

“指向T::SubType”的指针。

如果上面没有使用关键词typename,SubType会被认为是class T的一个static成员,于是编译器理解一个具体变量或一个对象,导致下面的语句:

T::SubType * ptr;

表达的意义变成:class T的一个静态static成员SubType与ptr相乘。

通常如果某个与template parameter相关的名称是个类型type时,就必须加上关键词typename。

1.1 迭代器应用

typename的一个典型应用是在template程序代码中使用STL容器供应的迭代器iterators:

#include

template

void printcoll(T const& coll)

{

        typename T::const_iterator pos;

        typename T::const_iterator end(coll.end());

        for(pos = coll.begin(); pos != end; ++pos) {

               std::cout << *pos <<  '  ';

        }

        std::cout << std::endl;

}

    在这个函数模板中, coll是个STL容器,其元素类型为T。这里使用了STL容器的迭代器类型iteration type遍历coll中的所有元素。迭代器类型为const_iterator,每个STL容器都声明有这个类型:

class stlcontainer {

        ...

        typedef        ...        iterator;

        typedef         ...        const_iterator;

        ...

};

使用模板类型template Type T的const_iterator时,必须写出全民并在最前面加上关键词typename:

typename T::const_iterator pos;

1.2 .template 结构体(construct)

考虑如下程序,其中使用标准的bitset类型:

template

void printBitset(std::bitset const& bs)

{

        std::cout << bs.template to_string, allocator >();

}

此例子中的 .template看起来古怪,但是如果没有它,编译器无法得知紧跟其后面的 “<”是template argument list的起始,而非一个小于号。这里的参数bs依赖于模板参数template paramter N。

结论是“.template” 或"->template"记号只在templates之内才能被使用,且必须紧跟着与模板参数相关的东西。

2 .使用this指针
如果class template拥有base class,那么其内出现的成员名称x并非总是等价与this->x,即使x系继承而来。例如:

template

class Base {

        public:

                void exit();

};

template

class Derived: public Base {

        public:

                void foo() {

                       exit();

                }

};

本例中,foo()内解析exit符号时,定义于Base的exit会被编译器忽略。因此,要么获得一个编译器错误,要么是调用外部额外写的exit()函数。

目前避免这样的情况有一个准则:

使用与template相关的符号时,建议总是以this->或Base::进行修饰。

3.成员模板Member Templates

    class的成员既可以是templates:既可以是nested class templates,也可以是member function templates。通常只有当两个stack类型相同,也就是当两个stacks拥有相同类型的元素时,你才能对它们相互赋值assign,也就是将某个stack赋值给另一个。不能把某种类型的stack赋值给另一种类型的stack,即使这两种类型之间可以隐式转型:

Stack intStack1, intStack2;

Stack floatStack;

...

intStack1 = intStack2;    //OK

floatStack = intStack1;  //ERROR: 两个stacks类型不同

然而,可以把assignment运算符定义为一个template,就可以把两个“类型不同,但其元素可以隐式转型的”堆栈互相赋值。为完成此时,Stack<>需要如下面声明:

template

class Stack {

private:

        std::deque elems;

public:

        void push(T const &);

        void pop();

        T top() const;

        bool empty() const {

               return elems.empty();

         }

        template

        Stack& operator= (Stack const&)

}

 相比原先的Stack,有如下改动:

1)增加一个assignment运算符,使Stack可被赋予一个拥有不同元素类型T2的Stack.

2)这个Stack使用Deque作为内部容器。

具体新增加的assignment运算符定义实现如下:

template

template

Stack& Stack::operator=  (Stack const& op2)

{

        if((void*)this == (void*) &op2) {

                return *this;

        }

        Stack tmp(op2);

        elems.clear();

        while(!tmp.empty()){

               elems.push_front(tmp.top());

                tmp.pop();

        }

        return *this;

}

在拥有template parameter T的模板中定义一个内层的inner模板参数template parameter T2:

template

template

...

现在有了这个赋值模板成员member template,就可以把一个int stack赋值给一个float stack:

Stack intStack1, intStack2;

Stack floatStack;

...

floatStack = intStack1;

这看起来绕过了类型检查,其实必要的类型检查还是有的,发生在源端的元素拷贝到目的端时进行:elems.push_front(tmp.top());

这里会将整数类型的元素转换成浮点类型元素。

因此试图将字符堆栈转换成浮点堆栈是不能成功的,因为string不能转换成float类型。

前面的模板赋值运算符并不取代default 赋值运算符。如果你在相同类型的Stack之间赋值,编译器还是会采用default assignment运算符。

这里,我们也可以把内部容器参数化:

template >

Class Stack{

private:

        CONT elems;

public:

        void push(T const&);

        void pop();

        T top() const;

        bool empty() const {

               return elems.empty();

        }

        template

        Stack& operator= (Stack const&);

};

此时,template assignment运算符实现如下:

template

template

Stack&     Stack::operator= (Stack const& op2)

{

        if((void*)this == (void*)&op2) {

               return *this;

        }

        Stack tmp(op2);

        elems.clear();

        while(!tmp.empty()) {

                elems.push_front(tmp.top());

                tmp.pop();

        }

        return *this;

}

4. 模板模板参数(双模板参数)Template Template parameters

    一个模板参数template parameter本身也可以是个类模板class template,这就是(类)模板(的)模板参数的由来,类模板的模板参数非常有用。

为了使用其他类型的元素容器,stack class使用者必须两次知道元素类型,一次是元素类型本身,另一次是容器类型:

Stack > vStack;

但是如果使用模板模板参数,就可以仅仅指明元素类型,无须再指定容器类型:

Stack vStack;

为了实现这种特性,你必须把第二个模板参数template parameter声明为模板(的)模板参数。

原则上代码可以写为:

template

                 template class CONT = std::deque >

Class Stack {

private:

        CONT elems;

public:

        void push(T const&);

        void pop();

        T top() const;

        bool empty() const {

                return elems.empty();

        }

};

与先前的差别是,第二个模板参数被声明为一个类模板class template,上面黑体部分,

template   class CONT

其默认值也由原先的std::deque 变更为std::deque. 这个参数必须是个class template,并以第一参数的类型完成实例化:

CONT elems;

但是这样编译还是有问题,这就是:

模板模板参数的匹配问题。

本例的问题在于:标准库中的std::deque template要求不只是一个参数:第二个参数是个配置器allocator,它虽然有默认值,但当它被用来匹配CONT的参数时,其默认值就被编译器强行忽略了。我们可以重写class声明语句。使得CONT参数要求带两个参数的容器:

template

                template

                                typename ALLOC = std::allocator >

                                class CONT = std::deque>

Class Stack {

 private:

        CONT  elems;

        ...

};

最后的Stack template实现如下。

1)stack.hpp

2) stacktest.cpp

5. 零值初始化

6. 字符串常数作为模板参数

7. 小结

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/979890.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号