C++类模板之双重模板参数(Template Template Parameters)

  • Post author:
  • Post category:其他


一个 模板参数 本身也可以是个 类模板。我们还是以Stack<T>为例子

为了使用其它类型的元素容器,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。

原 则上程序代码可以写为:

// basics/stack7decl.cpp
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 顶端元素
	bool empty() const { // stack 是否为空
		return elems.empty();
	}
};

与先前的 stack 差别在于,第二个 template parameter 被声明为一个 class template:

template <typename ELEM> class CONT

注意:CONT定义的是一个 class 类型,因此


必须使用关键词 class


来声明它。所以,下面代码是


错误的

template <typename T,
template <typename ELEM> typename CONT = std::deque > //ERROR
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); // 追加元素
}

这就完成了吗?如果你试图使用上述新版 Stack,编译器会报告一个错误

严重性    代码    说明    项目    文件    行    禁止显示状态
错误(活动)    E0999    类模板 "std::deque" 与 模板 template 参数 "CONT" 不兼容   

问题出在 template template argument 不但必须是个 template, 而且其参数必须严格匹配它所替换的 template template parameter 的参数。标准库中的 std::deque template 要求

不只一个参数

。第二参数是个配置器(allocator),它虽有默认值,但当它被用来匹配CONT 的参数时,其默认值会被编译器强行忽略了。

正确的代码如下所示

template <typename T,
template <typename ELEM,
typename ALLOC = std::allocator<ELEM> >class CONT = std::deque>
class Stack {
private:
CONT<T> elems; // 元素
...
};
由于 ALLOC并未在程序代码中用到,或者你也可以把它省略掉。
template <typename T,
	template <typename ELEM,typename = std::allocator<ELEM> >class CONT = std::deque>
	class Stack {
	private:
		CONT<T> elems; // 元素
	public:
		...
		}


最终的代码:

#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 的顶端元素
		bool empty() const { // stack 是否为空
			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

使用

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();
	}
}



版权声明:本文为sinat_31608641原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。