与函数类似,类也可以使用模板参数。典型例子就是cpp标准库里的各种容器。
Takeaways:
- 模板类的成员函数只在被使用到时才实例化
2.1 例子:栈
#include <vector>
template<typename T>
class Stack {
private:
std::vector<T> elems_;
public:
void push(const T& elem);
void pop();
T const& top() const;
bool empty() const {return elems_.empty();}
};
template<typename T>
void Stack<T>::push(const T& elem) {
// append copy of passed elem
elems_.push_back(elem);
}
template<typename T>
void Stack<T>::pop() {
assert(!elems_.empty());
elems_.pop_back();
}
template<typename T>
T const& Stack<T>::top() const {
assert(!elems_.empty());
return elems_.back();
}
没什么特别需要注意的,下面的代码是如何使用:
#include "stack1.hpp"
#include <string>
#include <iostream>
int main(int argc, char const *argv[]) {
Stack<int> intStack;
Stack<std::string> stringStack;
intStack.push(7);
std::cout<<intStack.top()<<std::endl;
stringStack.push("hello");
std::cout<<stringStack.top()<<std::endl;
stringStack.pop();
return 0;
}
对于模板类,成员函数只在被使用到时才实例化。如上代码中,pop()
只对stringStack
实例化了,因为它并未被intStack
调用。
2.3 模板类的部分使用
由于成员函数只有被调用才会实例化,我们不需要保证每个成员函数都对某个类型可用。
例如,我们可以给 stack 添加一个 str() 方法来返回自身的字符串。
template<typename T>
std::string Stack<T>::str() const {
std::stringstream ss;
ss << '[';
for (int i=0; i<elems_.size(); i++) {
ss << elems_[i];
if (i!=elems_.size()-1) {
ss << ',';
}
}
ss << ']';
return ss.str();
}
不支持 <<
操作符的类型同样可以使用这个模板类。
Stack< std::pair<int, int> > pairStack;
pairStack.push({4, 5});
pairStack.push({6, 7});
pairStack.pop();
std::cout<<pairStack.top().first<<std::endl; // output 4
std::cout<<pairStack.top().second<<std::endl; // output 5
但是如果尝试使用 str()
函数就会报错:
std::cout<<pairStack.str()<<std::endl; // compile error
2.4 友元
略,与模板关系不大
2.5 模板类的特化
类似于模板函数的重载,我们可以给出指定模板类的某些模板参数进行特化。特化可以使我们为某个模板实例单独实现一套逻辑。
为了特化模板类,我们必须用 template<>
,并使用具体的类替换 T
。如下就是对 std::string
的特化:
template<>
class Stack<std::string> {
...
};
类似的,我们也需要把成员函数中所有的 T
换为 std::string
。
2.6 部分特化
我们也可以使模板类“部分”特化来处理一类的类型。例如,对指针类型:
template<typename T>
class Stack<T*> {
...
};
注意,这里我们仍然保留了 template<typename T>
, 但是对指针进行了特化:class Stack<T*>
。
多参数的部分特化
对于多个模板参数的模板类,例如:
template<typename T1, typename T2>
class MyClass {
...
};
我们可以进行多种特化:
// partial specialization: both template parameters have same type
template<typename T>
class MyClass<T, T> {
...
};
// partial specialization: second type is int
template<typename T>
class MyClass<T, int> {
...
};
// partial specialization: both parameters are pointers
template<typename T1, typename T2>
class MyClass<T1*, T2*> {
...
};
对于不同的调用,会匹配不同的特化:
MyClass<int, float> mif; // uses MyClass<T1, T2>
MyClass<float, float> mff; // uses MyClass<T, T>
MyClass<float, int> mfi; // uses MyClass<T, int>
MyClass<int*, float*> mpp; // uses MyClass<T1*, T2*>
但是如果某种类型可以匹配多种特化,则会报错:
MyClass<int, int> mii; // ERROR, matches <T, T> and <T, int>
MyClass<int*, int*> mpp; // ERROR, matches <T, T> and <T1*, T2*>
遇到这种情况,需要添加更佳匹配的特化,例如:
// partial specialization: both parameters are pointers
template<typename T>
class MyClass<T*, T*> {
...
};
2.7 默认模板参数
略
2.8 类型别名
using
的用法,略
2.9 模板参数类型推断
C++17 新引入的功能:
Stack<int> intStack1;
Stack<int> intStack2 = intStack1; // OK in all versions
Stack intStack3 = intStack1; // OK since C++17
语法糖,略。
网友评论