tags:
- Cpp
Generics Programming in C++ (NC)
Do this at first: Type Deduction - Template Type Deduction
C 语言不支持函数重载(function overloading),这就意味着我们必须给不同的函数起不同的函数名,这对于完成相同任务但接收不同参数的函数来说非常麻烦。而 C++ 从一开始就支持函数重载,允许代码实现静态多态性。通过传递不同的参数,编译器会调用相应的函数。
函数重载当然是一个伟大的特性,但是如果我们要重载许多函数,我们就需要复制粘贴许多遍。但使用函数重载会给我们带来一些潜在问题。除了复制粘贴许多遍之外,我们还可能碰到一些未定义重载函数的情况,比如下面我们使用 long long sum_ll = sum(5342ll, 5864ll);
我们并没有定义相关的函数重载,编译器就不知道调用哪个函数。
函数重载当然是一个伟大的特性,但是如果我们要重载许多函数,我们就需要复制粘贴许多遍。但使用函数重载会带来一些潜在问题。除了需要复制粘贴许多遍之外,我们还可能碰到一些未定义重载函数的情况,比如下面我们使用 long long sum_ll = sum(5342ll, 5864ll);
我们并没有定义相关的函数重载,编译器就不知道调用哪个函数。
int add(int a, int b){
return a + b;
}
float add(float a, float b){
return a + b;
}
double add(double a, double b){
return a + b;
}
// ...
int main(){
int sum_i = add(20, 30); // Call int add(int a, int b)
float sum_f = add(3.14f, 5.58f); // Call float add(float a, float b)
double sum_d = sum(3.254, 2.546); // Call double add(double a, double b)
long long sum_ll = sum(5342ll, 5864ll); // Which to call? It's ambiguous!
return 0;
}
而在 C++98 后,函数模板(泛型函数)的出现避免了多次代码的复制粘贴。模板相当于一个你编写的蓝图,编译器在编译代码的时候会根据你提供的蓝图帮你生成重载函数。从而,你不需要记忆你到底重载了哪些函数。
// Compiler will generate function code at compile-time
// And function template is not a function btw
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int sum_i = add<int>(20, 30); // Same as add(20, 30) expicitly
float sum_f = add<float>(3.14f, 5.58f); // Same as add(3.14f, 5.58f)
double sum_d = add<double>(3.254, 2.546); // Same as add(3.254, 2.546)
long long sum_ll = add<long long>(5342, 5864); // Same as add(5232ll, 5864ll)
return 0;
}
上面的例子中,编译器帮我们实例化生成了四个重载函数。在 C++17 后,你实际上可以不再显式地提供模板重载类型,编译器会帮你推导相关的类型。
每个模板类型都至少被类型推断一次
multiple template parameters and non object type params
with auto
Variadic arguments and Variadic Function Templates
reference collapsing
同样,类模板也不是一个类。它是让编译器帮我们创建类的模板蓝图。
STL - you use all the time
class myContainer {
public:
myContainer(int N) {
m_data = new int[N];
}
~myContainer() {
delete[] m_data;
}
private:
int* m_data;
};
class myContainer {
public:
myContainer(int N) {
m_data = new float[N];
}
~myContainer() {
delete[] m_data;
}
private:
float* m_data;
};
template<typename T>
class myContainer {
public:
myContainer(int N) {
m_data = new T[N];
}
~myContainer() {
delete[] m_data;
}
private:
T* m_data;
};
还可以提供一个 non-type object
#include <cstddef> // For size_t
template<typename T, size_t N>
class myContainer {
public:
myContainer() {
m_data = new T[N];
}
~myContainer() {
delete[] m_data;
}
private:
T* m_data;
};
int main() {
myContainer<int, 5> container;
return 0;
}
typeid().name()
#include <vector>
// Without alias templates:
typedef std::vector<int> myvec_int; // C++03 alias syntax
typedef std::vector<float> myvec_float; // C++03 alias syntax
// With alias templates:
template<typename T>
using myvec = std::vector<T>; // C++11 syntax
int main(){
myvec_int vi = {1, 2, 3, 4, 5}; // Using the old alias syntax
myvec_float vf = {1.1f, 2.2f, 3.3f}; // Using the old alias syntax
myvec<int> vi_alias = {6, 7, 8, 9, 10}; // Using the new alias template syntax
myvec<float> vf_alias = {4.4f, 5.5f, 6.6f}; // Using the alias template syntax
return 0;
}
Alias template cannot be specialized.
模板的参数并不必须为一种”类型“:
template<class T, size_t N>
class Array{
T m_data[N];
// ...
};
Array<foobar, 10> some_foobars;
NTTPs中的常数类型必须在编译阶段或链接时确定好,而且类型必须为: