member template
- 所谓成员模板就是一个类的成员函数是模板。类似下面这样:
template <class T>
class Stack{
private:
vector<T> v_;
template <class T2>
Stack<T>& operator=(const Stack<T2>&);
};
template <class T>
template <class T2>
Stack<T> Stack<T>::operator=(const Stack<T2>& st){
...
}
- 可以看到成员模版的语法:在函数上面再来一层template,类似模板函数的声明。而它的实现就是将使用的template逐层写出,然后再来实现。
- 你可能会问,这有什么用?
template <class _InputIterator>
list(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{ insert(begin(), __first, __last);
- 上面就是SGI下容器list的关于迭代器的构造函数。可以看到使用了迭代器。所谓的_InputIterator就是输入型迭代器,这来源于STL的命名风格:参数的命名使用接口可以接纳的最弱的迭代器。
- 这使得我们可以使用vector等一系列容器构造list,只要它的迭代器强于_InputIterator。
- 成员模板一个显著的作用就是支持容器的隐式类型转换,没错是容器的。
template<class T>
class Stack{
private:
std::vector<T> v_;
public:
void pop();
void push(const T&);
T& top();
std::size_t size()const;
bool empty();
Stack();
Stack<T>& operator=(const Stack<T>&);
};
Stack<int> intSt;
intSt.push(1);
Stack<float> floatSt;
floatSt.push(2.22);
intSt = floatSt;
- 上面的最后一行代码会出现问题,即使float可以隐式转换成int,但是Stack不可以隐式转换成Stack。因为没有这样的operator=函数。没错,有了上面的栗子,你应该很快能反映过来!我需要operator的右操作数的类型是更高阶的可变!
- 这就轮到成员模板大显身手了。
template<class T>
class Stack{
template<class T2>
Stack<T>& operator=(const Stack<T2>&);
};
- 上面的operator=就是一个成员模板函数,它支持隐式类型转换。而且你不需要保留老版本的operator=来应付非隐式类型转换,因为当两个Stack = Stack进行转换时,T和T1都会被推导成int。
- 现在你的Stack = Stack会被成功编译。
模板的模板形参
- 模板的形参既可以是type,non-type,也可以是模板。
- 没错,像下面这样,
template<class T, template<class T2> class _Con = std::deque>
class Stack{
private:
_Con<T> c_;
...
};
- 可以看到第一行中有3个class,第一个class正常声明模板类型参数,而第二个class代表_Con这个模板形参需要的模板形参(细细品品,可能有点绕)!!这两个class都可以被typename替换,但是第三个class表示_Con是一个类名!!不能被typename替换!
- 依据此,我们可以重写Stack类。
template<class T, template<class EL> class _Con = std::deque>
class Stack {
private:
_Con<T> _c;
public:
void pop();
void push(const T&);
bool empty();
std::size_t size();
T& top();
template<typename T2,template<typename,typename> class _Con2>
Stack<T, _Con>& operator= (const Stack<T2, _Con2>&);
};
- 但是,你会发现编译不过去。因为我们的deque默认有两个模板形参,第二个正是我们的空间配置器,虽然它有缺省实参,但是模板的模板参数再传参是,编译器会忽略缺省实参!我们需要显式的写出模板参数来。
- 据此,我们可以写出一个包含成员模板和模板的模板参数的Stack类。
template<class T, template<class EL, class = std::allocator<EL>> class _Con = std::deque>
class Stack {
private:
_Con<T> _c;
public:
void pop();
void push(const T&);
bool empty();
std::size_t size()const;
T& top();
template<typename T2,template<typename,typename> class _Con2>
Stack<T, _Con>& operator= (const Stack<T2, _Con2>&);
};
template<class T, template<class, class> class _Con>
void Stack<T, _Con>::pop() {
_c.pop_back();
}
template<class T, template<class, class> class _Con>
void Stack<T, _Con>::push(const T& tmp) {
_c.push_back(tmp);
}
template<class T, template<class, class> class _Con>
bool Stack<T, _Con>::empty() {
return _c.empty();
}
template<class T, template<class, class> class _Con>
std::size_t Stack<T, _Con>::size()const {
return _c.size();
}
template<class T, template<class, class> class _Con>
T& Stack<T, _Con>::top() {
return _c.back();
}
template<class T, template<class, class> class _Con>
template<class T1, template<class, class> class _Con1>
Stack<T, _Con>& Stack<T, _Con>::operator=(const Stack<T1, _Con1>& rhs) {
if ((void*)&rhs == (void*)this) {
return *this;
}
Stack<T1, _Con> tmp(rhs);
_c.clear();
while (!tmp.empty()) {
_c.push_front(tmp.top());
tmp.pop();
}
return *this;
}