c++ 类模板的使用
1、菰灞巴静举例子除了可以申明函数模板外,也还可以声明类模板。举个例子(实现一个堆栈):template<typename T&爿讥旌护gt;class Stack {std::vector<T> v;public:Stack();Stack(const Stack<T>&); // T是同一类型的类模板才能拷贝Stack<T>& operator=(const Stack<T>&);void push(const T&);void pop();const T& top() const;bool empty() const;};template<typename T>Stack<T>::Stack(){}template<typename T>Stack<T>::Stack(const Stack<T>& rhs) : v(rhs.v){}template<typename T>Stack<T>& Stack<T>::operator=(const Stack<T>& rhs){v = rhs.v;return *this;}template<typename T>void Stack<T>::push(const T& x){v.emplace_back(x);}template<typename T>void Stack<T>::pop(){assert(!v.empty());v.pop_back();}template<typename T>const T& Stack<T>::top() const{assert(!v.empty());return v.back();}template<typename T>bool Stack<T>::empty() const{return v.empty();}
2、定义好类模板后,它的调用方法:int main(){using IntStack = Stack<int>; // typedef Stack<int> IntStackIntStack intStack; // Stack<int> intStackintStack.push(42);std::cout << intStack.top(); // 42Stack<std::string> stringStack;stringStack.push("hi");std::cout << stringStack.top(); // histringStack.pop();}
3、除了int类型外,模板实参可以是任何类型Stack<double*> doublePtrStack;Stack<Stack<int>> intStackStack;
4、成员函数只有被调用到时才实例化如果类模板有static数据成员,每种实例化类型都会实例化static数据成员。static成员函数和数据成员只被同类型共享template<typename T>class A {static std::size_t n;public:static std::size_t count();};template<typename T>std::size_t A<T>::n = 0;A<std::string> a; // 实例化A<std::string>::nA<int> b, c, d; // 实例化A<int>::n,bcd共享A<int>::count()和A<int>::nstd::size_t n = A<int>::count(); // 实例化A<int>::count()n = b.count(); // 使用A<int>::count()n = A::count(); // 错误:必须指定模板参数,否则无法得知实例化版本
5、类喋碾翡疼模板的部分使用由于成员函数只有被调用到时才实例化,模板实参只要提供必要的操作,而非所有需要的操辑湃形傥作。如Stack提供一个printOn对每个元素调用operator<<,即使没有对元素定义operator<<也能使用这个类。只有调用printOn时才会产生错误,因为这时不能对这些元素实例化operator<<template<typename T>class Stack {...void printOn(std::ostream&) const;};template <typename T>void Stack<T>::printOn(std::ostream& os) const{for (const T& x : v) os << x << ' ';}Stack<std::pair<int, int>> s; // std::pair没有定义operator<<s.push({1, 2}); // OKs.push({3, 4}); // OKstd::cout << s.top().first << s.top().second; // 34s.printOn(std::cout); // 错误:元素类型不支持operator<<
6、与其使用printOn函数打印元素,不如重载operator<<,然而通常operator<<会实现为非成员函数。下面在类内定义友元,它是一个普通函数template<typename T>class Stack {...void printOn(std::ostream& os) const;friend std::ostream& operator<<(std::ostream& os, const Stack<T>& stack) {stack.printOn(os);return os;}};
7、如果在类外定义友元,类模板参数不可见,事情会复杂很多有两个解决方案,一是隐式声明一个新的函数模板,并使用不同的模板参数template<typename T>class Stack {…template<typename U>friend std::ostream& operator<<(std::ostream&, const Stack<U>&);};// 类外定义template<typename U>std::ostream& operator<<(std::ostream& os, const Stack<U>& stack){stack.printOn(os);return os;}
8、二是将友元前置声明为模板,而友元参数中包含类模板,这样就必须先前置声明类模板template<type荏鱿胫协name T> // operator<<中参数中要求Stack模板可见class Stack;template<typename T>std::ostream& operator<<(std::ostream&, const Stack<T>&);// 随后就可以将其声明为友元template<typename T>class Stack {…friend std::ostream& operator<< <T> (std::ostream&, const Stack<T>&);};// 类外定义template<typename T>std::ostream& operator<<(std::ostream& os, const Stack<T>& stack){stack.printOn(os);return os;}
9、同样,函数只有被调用到时才实例化,元素没有定义operator<<时也可以使用这个类,只有调用operator<<时才会出错Stack<std::pair<int, int>> s; // std::pair没有定义operator<<s.push({1, 2}); // OKs.push({3, 4}); // OKstd::cout << s.top().first << s.top().second; // 34std::cout << s << '\n'; // 错误:元素类型不支持operator<<