Template Template Parameters(双重模板参数)
来源:互联网 发布:淘宝小白帽 编辑:程序博客网 时间:2024/05/18 23:15
一、需求引入
一个 template parameter 本身也可以是个 class template , 这一点非常有用 。 我们将再次以 stack class template 说明这种用法。
为了使用其它类型的元素容器 , stack class 使用者必须两次指定元素类型: 一次是元素类型本身,另一次是容器类型:
Stack<int,std::vector<int> > vStack; // int stack,以vector为容器
如果使用 template template parameter,就可以只指明元素类型,无须再指定容器类型:
Stack<int,std::vector> vStack; // int stack,以vector为容器
二、示例代码
为了实现这种特性,你必须把第二个 template parameter 声明为 template template parameter
原则上程序代码可以写为:
template <typename T, template <typename ELEM> class CONT = std::deque >
class Stack {
private:
CONT<T> elems; // 元素
public:
void push(T const&); // push 元素
void pop(); // pop 元素
T top() const; // 传回 stack 顶端元素
// stack 是否为空
bool empty() const {
return elems.empty();
}
};
与先前的 stack 差别在于,第二个 template parameter 被声明为一个 class template:
template <typename ELEM> class CONT
其默认值则由 std::deque<T> 变更为 std::deque。这个参数必须是个 class template,并以第一参数的类型完成实例化:
CONT<T> elems;
本例「以第一个 template parameter 对第二个 template parameter 进行实例化」只是基于例子本身 的需要。实际运用 时你可以使用 class template 内的任何类型来实例化一个 template template parameter。
和往 常一 样,你也 可以改 用 关键词 class 而不使 用关键 字 typename 来 声明一个 template parameter;但 CONT 定义的是一个 class 类型,因此你必须使用关键词 class 来声明它。
所以,下面的程序代码是正确的:
template <typename T, template <typename ELEM> class CONT = std::deque > //OK
class Stack {
...
};
下面的程序代码则是错误的:
//ERROR
template <typename T, template <typename ELEM> typename CONT = std::deque >
class Stack {
...
};
由于 template template parameter 中的 template parameter 实际并未用到 , 因此你可以省略其名称 :
template <typename T, template <typename> class CONT = std::deque >
class Stack {
...
};
所有成员函数也必须按此原则修改:必须指定其第二个template parameter为template template parameter。同样的规则也适用于成员函数的实作部份。
例如成员函数 push()应该实作如下:
template <typename T, template <typename> class CONT>
void Stack<T,CONT>::push (T const& elem) {
elems.push_back(elem); // 追加元素
}
另请注意,function templates 不允许拥有 template template parameters。
三、拓展:Template Template Argument 的 匹配(matching)
如果你试图使用上述新版 Stack,编译器会报告一个错误:默认值 std::deque 不符合 template template parameter CONT 的要求。问题出在 template template argument 不但必须是个 template,而且 其参数 必须严格匹配它 所替换 之 template template parameter 的 参数。 Template template argument 的默认值不被考虑,因此如果不给出拥有默认值的自变量值时,编译器会认为匹配失败。
本例的问题在于:标准库中的 std::deque template 要求不只一个参数。第二参数是个配置器(allocator),它虽有默认值,但当它被用来匹配CONT 的参数时,其默认值被编译器强行忽略了。
办法还是有的。我们可以重写 class 声明语句,使 CONT 参数要求一个「带两个参数」的容器:
template <typename T, template <typename ELEM, typename ALLOC = std::allocator<ELEM>> class CONT = std::deque >
class Stack {
private:
CONT<T> elems; // 元素
...
};
由于ALLOC 并未在程序代码中用到,因此你也可以把它省略掉。
四、最终代码
Stack template 的最终版本如下。此一版本支持对「不同元素类型」之 stacks 的彼此赋值动作:
#ifndef STACK_HPP
#define STACK_HPP
#include <deque>
#include <stdexcept>
#include <memory>
template <typename T, template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque >
class Stack {
private:
CONT<T> elems; // 元素
public:
void push(T const&); // push 元素
void pop(); // pop 元素
T top() const; // 传回 stack 的顶端元素
// stack 是否为空
bool empty() const {
return elems.empty();
}
// 赋予一个「元素类型为 T2」的 stack
template<typename T2, template<typename ELEM2, typename = std::allocator<ELEM2> > class CONT2 >
Stack<T,CONT>& operator= (Stack<T2,CONT2> const&);
};
template <typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::push (T const& elem) {
elems.push_back(elem); // 追加元素
}
template<typename T, template <typename,typename> class CONT>
void Stack<T,CONT>::pop () {
if (elems.empty()) {
throw std::out_of_range("Stack<>::pop(): empty stack");
}
elems.pop_back(); // 移除最后一个元素
}
template <typename T, template <typename,typename> class CONT>
T Stack<T,CONT>::top () const {
if (elems.empty()) {
throw std::out_of_range("Stack<>::top(): empty stack");
}
return elems.back(); // 传回最后一个元素的拷贝
}
template <typename T, template <typename,typename> class CONT>
template <typename T2, template <typename,typename> class CONT2>
Stack<T,CONT>&
Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2) {
//是否赋值给自己
if ((void*)this == (void*)&op2) {
return *this;
}
Stack<T2,CONT2> tmp(op2); // 创建 assigned stack 的一份拷贝
elems.clear(); // 移除所有元素
//复制所有元素
while (!tmp.empty()) {
elems.push_front(tmp.top());
tmp.pop();
}
return *this;
}
#endif //STACK_HPP
下面程序使用了上述最终版本的 stack:
#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include "stack8.hpp"
int main() {
try {
Stack<int> intStack; // stack of ints
Stack<float> floatStack; // stack of floats
//操控int stack
intStack.push(42);
intStack.push(7);
//操控 float stack
floatStack.push(7.7);
// 赋予一个「不同类型」的 stack
floatStack = intStack;
// 打印 float stack
std::cout << floatStack.top() << std::endl;
floatStack.pop();
std::cout << floatStack.top() << std::endl;
floatStack.pop();
std::cout << floatStack.top() << std::endl;
floatStack.pop();
} catch (std::exception const& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}
// int stack,以 vector 为其内部容器
Stack<int,std::vector> vStack;
vStack.push(42);
vStack.push(7);
std::cout << vStack.top() << std::endl;
vStack.pop();
}
程序运行的输出结果为:
7
42
Exception: Stack<>::top(): empty stack
7
注意,template template parameter 是极晚近才加入的C++ 特性,因此上面这个程序可作为一个 极佳工具,用来评估你的编译器对 template 特性的支持程度。
- Template Template Parameters(双重模板参数)
- Nontype Class Template Parameters(非类型类模板参数)
- Nontype template parameters(非型别模板参数)
- c++ template笔记(3)非类型模板参数nontype template parameters
- Template template parameter(模板参数) example
- variadic template (可变参数模板)
- C++ - 非类型模板参数(nontype template parameters) 使用 及 代码
- C++ - 非类型模板参数(nontype template parameters) 使用 及 代码
- Template(模板)
- 模板模式(Template)
- 模板(Template)
- 模板(Template)
- Template(模板)
- 模板(Template)
- 模板(Template)
- C++模板(template)
- C++模板(template)
- Template parameters (C++ only)
- 搭建ipv6环境
- 在shell脚本中获取上个月最后一天的日期
- 每天一个linux命令(8):cp 命令
- linux安装mysql5.7版本
- React createClass 和 Component 有什么区别
- Template Template Parameters(双重模板参数)
- Spring和SpringMVC父子容器关系初窥
- Android studio 点击按钮跳转到新的Activity
- 每天一个linux命令(9):touch 命令
- idea中properties配置文件中显示utf-8编码时,怎么办?
- Path Sum II
- java 处理高并发(转载)
- 解决powerdesigner连接mysql时连接不上的问题
- Java 遍历List ConcurrentModificationException 异常解析