模板使用技巧基础
这篇文章主要记下实际工程中使用模板的一些基本技巧,其中主要包括typename关键字的使用,定义模板成员函数,定义嵌套类模板,双重模板类型参数(template template parameter),以及零值初始化的技巧。
一、关键字typename
关键字typename是C++标准化过程中被引入的,目的是告诉编译器模板类型参数或者其内的某个标识符是个类型。
template <typename T>
class MyClass{
public:
typename T::subtype *ptr;
... ...
};
第二个typename关键字说明subtype是T内部定义的一个类型,所以ptr是一个指向subtype的指针。如果移除掉typename关键字修饰,那么subtype会被编译器误认为是T内部的一个静态成员,所以 T::subtype * ptr 就变成了一个表达式。
通常如果某个与模板参数相关的名称是个类型时,前面就需要添加关键字typename.
二、.template构件
在引入typename之后,又出现了一个类似问题。如下面的代码所示
template<int N>
void printBitset (std::bitset<N> const& bs)
{
std::cout << bs.template to_string<char,char_traits<char>,
allocator<char> >();
}
.template构件显得比较古怪,如果没有它,编译器不会认为紧跟其后的‘<’ 是模板参数列表的开始,而非一个小于号。
小结:.template构件只有在其前的构件(本例是bs)依赖于模板类型参数时,才会被使用。
三、使用this->
template <typename T>
class Base {
public:
void exit();
};
template <typename T>
class Derived : Base<T> {
public:
void foo() {
exit(); // calls external exit() or error
}
};
如果class templates拥有base classes,那么其出现的成员名称'exit()'并非总是等价于‘this->exit()',即base<T>::exit()。应在调用前,显式添加’this->' 或‘base<T>::'才可通过编译。
使用建议准则:任何使用基类继承而来的成员或成员函数,如果基类与模板类型参数相关,那么最好在使用前加上'this->'或者’base<T>::'的修饰。
四、成员模板
成员模板可以是嵌套模板类也可以是成员函数模板。
// basics/stack6decl.hpp
template <typename T, typename CONT = std::deque<T> >
class Stack {
private:
CONT elems; // elements
public:
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 elems.empty();
}
// assign stack of elements of type T2
template <typename T, typename CONT2>
Stack<T,CONT>& operator= (Stack<T,CONT2> const&);
};
// basics/stack6assign.hpp
template <typename T, typename CONT>
template <typename T, typename CONT2>
Stack<T,CONT>&
Stack<T,CONT>::operator= (Stack<T,CONT2> const& op2)
{
if ((void*)this == (void*)&op2) { // assignment to itself?
return *this;
}
Stack<T> tmp(op2); // create a copy of the assigned stack
elems.clear(); // remove existing elements
while (!tmp.empty()) { // copy all elements
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}
通过上述成员赋值模板函数,就可完成以deque实现的stack赋值到以vector实现的stack,不过stack中保存的元素类型仍然相同。
五、双重模板类型参数(Template Template Parameter)
template <typename T, typename CONT = std::deque<T> >
class Stack {
...
};
如此声明,在使用不同container作为stack的实现时,需要再次重复指定元素类型。如果使用ttp的话,那么就可以不需要重复指定元素类型了。那么ttp到底是个什么东东呢?实际上ttp就是在声明时告诉编译器模板类型参数本身也是一个模板类。按照ttp原则,这个例子中,stack的container模板类型参数本身也是一个类模板。所以修改如下
template<
typename T,
template<typename ELEM>class CONT=std::deque
>
class Stack {
... ...
};
如果按照ttp声明stack模板类时,那么在使用不同container作为stack的实现时,就不再需要重复指定元素类型了。
技巧1:因为TTP中的模板类型参数(ELEM)并没有被使用,所以形式上可以忽略。所以下面的这个声明也可以
template<
typename T,
template<typename>class CONT=std::deque
>
class Stack {
... ...
};
注意事项:
成员函数模板的声明及实现中的模板类型参数也需要按照TTP原则做出相同的改动。
模板模板参数的匹配(Template Template Argument Matching)
如果试图使用按照TTP原则修改后的class templates stack,编译器会报错说std::deque不符合template template parameter CONT的要求,原因是TTP原则不仅要求template parameter本身是个class template,而且这个class template的参数必须严格匹配它所替换的TTP的参数。
再次考虑本例,原本代表container的那个template parameter是 std::deque<T, alloc<T> >, 很显然这个container是需要两个template parameter的,但是当我们之前用以替换的class template却只声明了一个template parameter,这就导致了参数匹配不成功。所以需要进一步修改一下class template Stack的声明如下
template <typename T,
template <typename ELEM, typename ALLOC = std::allocator<ELEM> >
class CONT = std::deque>
class Stack {
private:
CONT<T> elems; // elements
…
};
PS: 似乎为了少写一个T, 却使class template声明变得很复杂。
六、零值初始化
template <typename T>
void foo()
{
T x = T(); // x has undefined value if T is built-in type
}
假定T并无默认构造函数时,x的初值就无法被正确初始化,进而引发未定义的行为。因此在定义class template时,一定要定义默认构造函数,初始化其内部成员。当然,如果T本身是内建类型时,如int, bool等, 当默认构造函数被调用后,其值就会被初始化为0。
小结:
1. 当要操作一个取决于template parameter的类型名称时,应该在前面加typename关键字修饰。
2. 嵌套类和成员函数也可以是模板。
3. 赋值运算符的template版本并不取代默认赋值运算符。
4. 把class template作为template parameter使用的技巧成为Template Template Parameter。
5. Template Template Arguments必须完全匹配其对应参数。
6. 当具现化一个内建类型的变量时,如果需要设定初值,必须明确调用其默认构造函数。
分享到:
相关推荐
这本书深入探讨了C++模板的各个方面,包括基本概念、模板元编程、模板特化、模板技巧以及如何有效地使用和调试模板代码。对于希望深入理解C++模板机制的开发者来说,这是一份宝贵的资源。 2. **C++ Templates 简体...
《C++ Templates(简体中文版)》是一本专注于C++模板编程的指南书籍,它不仅为读者提供了模板的基本概念,还涵盖了常用技巧和应用实例,帮助读者构建坚实的C++模板知识基础。C++模板是该语言强大功能的体现,允许...
根据提供的信息,我们可以总结出以下关于《C++ Templates: The Complete Guide》这本书的关键知识点: ### 书籍基本信息 - **书名**:C++ Templates: The Complete Guide - **作者**:David Vandevoorde 和 ...
1. **模板基础**:包括模板的声明、实例化以及如何使用模板参数推断。 2. **模板特化与偏特化**:当通用模板不能满足特定需求时,可以通过特化或偏特化来提供更精确的实现。 3. **模板元编程**:使用模板作为编译时...
例如,定义`TEMPLATE_DIRS`时,可以使用`BASE_DIR + '/templates'`。 2. **利用{% url %}模板标签**:在模板中使用{% url %}替代硬编码的URL,这样当项目结构发生变化时,链接依然可用。通过将视图函数与URL模式...
通过系统地学习《C++ Templates-The Complete Guide》,读者不仅可以获得扎实的理论基础,还能掌握实用的编程技巧,为开发高质量的C++软件打下坚实的基础。无论是对于初学者还是有经验的开发者来说,这本书都是不可...
在阅读本书之前,建议读者已经具备一定的C++基础知识,包括类、继承等面向对象编程概念,以及标准库的使用。对于C++标准,本书主要依据1998年的标准,并考虑了2002年技术勘误的内容。如果读者在C++基础方面有所欠缺...
《C++ Templates - The Complete Guide》这本书旨在向软件架构师和工程师们清晰地展示如何正确地使用C++模板,以构建和维护更清洁、更快、更智能的软件。 首先,书中通过一个富有洞察力的教程介绍了C++模板的基础...
- **第一部分(第2~7章)**:介绍了模板的基本概念,包括模板类和模板函数的定义、使用方法、语法特点等基础知识。 - **第二部分(第8~13章)**:详细讲解了模板的语言细节,如模板参数、模板特化、模板元编程等高级...
- **表达式模板(Expression Templates)**:一种优化技巧,用于提高模板类的性能。 - **元编程(Template Metaprogramming)**:使用模板在编译时执行计算。 - **类型函数(Type Functions)**:通过模板实现的函数,...
《C++ Templates中文版》是C++模板编程的完全指南,旨在通过基本概念、常用技巧和应用实例三方面的有用资料,为读者打下C++模板知识的坚实基础。书中详细讲解C++模板语言的概念,使用C++模板的常用设计技巧,还运用...
10. **模板的编译期开销与技巧**:虽然模板提供了强大的功能,但过度使用可能导致编译时间增加和代码膨胀。合理地使用模板,如限制模板深度、避免模板模板参数等,可以减轻这些问题。 配合书本配套的源码,读者可以...
本书是C++模板编程的完全指南,旨在通过基本概念、常用技巧和应用实例3方面的有用资料,为读者打下C++模板知识的坚实基础。 全书共22章。第1章全面介绍了本书的内容结构和相关情况。第1部分(第2~7章)以教程的风格...
总之,《C++ Templates全览》是一本深度解析C++模板技术的权威之作,适合有一定C++基础并希望深入学习模板的开发者阅读。通过这本书,读者不仅可以掌握模板的基本用法,还能了解到模板背后的原理和高级技巧,从而在...
本书是一本对基本概念和语言特性...·从基础的到早前没有文字约定的惯用语法和技巧; ·如何在不对性能和安全性造成损害的前提下进行源代码重用; ·如何提高C++程序的效率; ·如何构建更具灵活性和可维护性的软件。
- **目标读者群**:本书面向的是两类读者群体:一是准备开始学习和使用 C++ Templates 的初学者;二是已经有丰富经验并希望进一步提升自己的专业开发者。 #### 三、书籍结构概述 - **第一篇:入门与基础** - **...
书中首先介绍了模板的基础知识,包括函数模板和类模板的声明与使用,如何通过模板参数推导简化编程,以及模板特化来处理特定类型的情况。此外,作者还深入讨论了模板元编程,这是一种利用模板进行编译时计算的技术,...