C16 Templates and Generic Programming
16.1 Defining a Template
OOP vs generic programming:
OOP deals with types that are not known until run time; Whereas in generic programming, the type is determined during compilation.
Templates are the foundation of generic programming
Define a Template:
Define a function template. A function template is a formula from which we can generate type-specific versions of that function.
In a template definition, the template parameter list cannot be empty.
template <typename T>
T add(T a, T b) {
return a+b;
}
class A{
friend ostream& operator<<(ostream& os, const A& rhs);
public:
A(int a): a_(a){};
A operator+(A& rhs) {
return this->a_ += rhs.a_;
}
private:
int a_;
};
ostream& operator<<(ostream& os, const A& rhs) {
os << rhs.a_ << endl;
return os;
}
int main() {
string s1 = "hello ";
string s2 = "world!";
cout << add(1,2) << endl;
cout << add(s1,s2) << endl;
cout << add(1.0,2.0) << endl;
A a1(1);
A a2(5);
cout << add(a1, a2) << endl;
return 0;
}
3
hello world!
3
6
Class Templates
- 初始化时,一定要表明T是什么类型
- 对于在类外实现的成员函数,一定要加上type! 类内的就不用!
- 对于静态成员,每一种不同的type都共享同一个静态成员,但是不同type之间不共享
template<typename T>
class A{
public:
T a;
A& func() {
cout << "1" << endl;
return *this;
}
A& func2();
static int count;
};
// 2. 不在类内实现函数,需要标T
template<typename T>
A<T>& A<T>::func2() {
cout << "2" <<endl;
return *this;
}
template<typename T>
int A<T>::count = 0;
int main() {
// explicitly specify the value
// 1. 初始化时,一定要表明T是什么类型
A<int> a1;
int c = a1.a;
a1.func(); // 1
a1.func2(); // 2
// 3. 静态成员,每一个type的模板共享一个!
A<long> a2;
A<int> a3;
a1.count += 1; // 1
a2.count += 10; // 10
cout << a1.count << endl; // 1
cout << a2.count << endl; // 10
cout << a3.count << endl; // 1
return 0;
}
template parameter
// F -> default template argument!
template<typename T, typename F = std::greater<T>>
int compare(const T& a, const T& b, F f = F()) {
if (f(a, b)) {
return 1;
}
if (f(b, a)) {
return -1;
}
return 0;
}
template<typename T>
bool isSmaller(const T& a, const T& b) {
return a < b;
}
int main() {
cout << compare(2,1) << endl; // 1, first T -> int
cout << compare(1,2, isSmaller<int>) << endl; // 1
return 0;
}
member template 模板嵌套
A class—either an ordinary class or a class template—may have a member function that is itself a template.
class A{
public:
template<typename T>
void func(T* p) {
cout << "I will delete the input pointer" <<endl;
delete p;
}
};
int main() {
A a;
a.func(new int(10)); // I will delete the input pointer
return 0;
}
16.2.5
Normal reference binding rules apply; and consts are low level, not top level.
const int* const a;
| |
low top
forward和右值
A function parameter, like any other variable, is an lvalue expression. 即函数的参数都默认视为左值
std::forward:
它通常用于实现完美转发(perfect forwarding).完美转发是指将函数参数以原样转发给另一个函数,包括值类别(左值或右值)和const限定符.
如果arg是右值,那么std::forward将返回一个右值引用;如果arg是左值,那么std::forward将返回一个左值引用;如果arg带有const限定符,那么std::forward将返回一个带有const限定符的引用
eg:
我们定义了三个f函数,分别接受一个左值引用、一个带有const限定符的左值引用和一个右值引用。然后我们定义了一个模板函数wrapper,它接受一个引用类型的参数x,并将其转发给f函数:
#include <utility>
void f(int& a) {
cout << "f(int&): " << a << endl;
}
void f(const int& a) {
cout << "f(const int&): " << a << endl;
}
void f(int&& a) {
cout << "f(int&&): " << a << endl;
}
template<typename T>
void warrper(T&& x) {
f(std::forward<T>(x));
}
int main() {
int a = 10;
const int b = 20;
warrper(a);
warrper(b);
warrper(30);
}
f(int&): 10
f(const int&): 20
f(int&&): 30
据实参的不同类型,wrapper函数会分别调用f(int&)、f(const int&)和f(int&&)
eg2:
template<typename T, typename t1, typename t2>
void flip(T f, t1 a, t2 b) {
f(b,a);
}
template<typename T, typename t1, typename t2>
void flip1(T f, t1&& a, t2&& b) {
f(b,a);
}
void func(int v1, int& v2) {
cout << v1 << " " << ++v2 << endl;
}
// rvalue -> match any type of arguments(lvalue, rvalue)
int main() {
int i = 10; // i is int&
int j = 20; // j is int&
flip(func, i, j); // 20 11
cout << i << endl; // 10, i 并没有改变
flip1(func, i, j); // 20 11
cout << i << endl; // 11, i 改变, 因为右值转为引用
}
16.3 overloading an template
template<typename T>
void print(T arg) {
cout << "I have only one parameter!" << endl;
cout << arg << endl;
}
template<typename T, typename N>
void print(T arg1, N arg2) {
cout << "I have two parameters!" << endl;
cout << arg1 << ",";
print(arg2);
}
int main()
{
int a = 1;
string s = "five";
print(a);
print(a,s);
}
I have only one parameter!
1
I have two parameters!
1,I have only one parameter!
five
16.4 variadic templates可变参数模板
在上面的例子中,如果我有多个参数会怎么样?我要把overload函数按照不同的参数数目重写吗?不用!用typename… arg 即可,来看:
template<typename T>
void print(T arg) {
cout << arg << endl;
}
template<typename T, typename... N>
void print(T arg1, N... arg2) {
cout << arg1 << ",";
print(arg2...);
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
string s = "five";
print(a,b,c,d,e);
print(a,b,c,d,s);
}
print函数模板有两个版本:第一个版本只接受一个参数,并将其输出到控制台;第二个版本接受两个或更多参数,第一个参数将被输出到控制台,然后递归调用print函数模板,将其余的参数作为参数包传递给它,继续进行输出操作。这个递归过程会一直进行下去,直到参数包中没有元素为止
16.5 template Specializations 模板特化
在模板定义中,针对某个特定的类型或值,定义一个特殊的版本(或多个版本)来处理该类型或值。模板特化允许我们为特定的类型或值提供特殊的处理方式,使代码更加灵活和通用
eg: 如何为不同类型的参数提供不同的处理方式:
#include <iostream>
#include <string>
using namespace std;
template<typename T>
void print(T arg) {
cout << arg << endl;
}
// 特化版本1,用于处理字符串类型的参数
template<>
void print<string>(string arg) {
cout << "The length of the string is " << arg.length() << endl;
cout << arg << endl;
}
// 特化版本2,用于处理char类型的参数
template<>
void print<char>(char arg) {
cout << "The ASCII code of the character is " << int(arg) << endl;
cout << arg << endl;
}
int main()
{
int a = 1;
double b = 3.14;
string s = "hello";
char c = 'A';
print(a);
print(b);
print(s);
print(c);
return 0;
}
1
3.14
The length of the string is 5
hello
The ASCII code of the character is 65
A