template,能让你定义一个待编译的模版 让编译器用一组规则为你编译代码

当我写一个函数时,我在函数中使用一个模板,我实际做的事类似蓝图的东西,当我决定调用那个函数时,指定一些参数,他们决定了实际放入模板的代码

简单例子#

#include <iostream> 

//template表明这是个编译时求值的模板,这不是实际的函数,只有在调用时才会创建并编译成源代码
//可以从实际参数中隐式获取T
template<typename T>
void Print(T value)
{
	std::cout << value << std::endl;
} 
int main()
{
	Print(5);
	//"5.5"被隐式转为const char[4]
	Print("5.5");
	Print(3.7);

	std::cin.get();
}

额外补充#

模板定义调用 Print(“5.5”) 时 T 的类型说明
void Print(T value)const char*发生退化。传递的是地址。
void Print(T& value)const char[4]不发生退化。传递的是数组的引用。
void Print(const T& value)char[4]不发生退化。T 本身是 char[4]。
// 它代表:我只接受一个含有 4个char的数组的“引用”
void Print(const char (&value)[4]) {
    // sizeof(value) == 4
    // 编译器知道这个数组刚好就是 4 个字节
}
//数组的首地址
void Print(const char* value) {
    // sizeof(value) == 8 (在64位系统上)
    // 编译器不知道这里有 4 个字符
}

例子2#

#include <iostream> 

//template表明这是个编译时求值的模板,这不是实际的函数,只有在调用时才会创建并编译成源代码(才会实际创建函数)
//可以从实际参数中隐式获取T
template<typename T>
//和上面一个意思
//template<class T>
void Print(T value)
{
	std::cout << value << std::endl;
}

int main()
{
	//显示指定T
	Print<int>(5);
	//让编译器自己推断T的类型
	Print(5);

	std::cin.get();
}

例子3#

演示前需要先把 Conformance mode改为 No(/permissive)

#include <iostream>
#include <string>

//模板只有在实际调用时才会创建
//编译器会根据实际调用情况自动创建函数
template<typename T>
void Print(T value)
{
    // 注意这里是 valu,少了一个 e
    std::cout << valu << std::endl;
}

int main()
{    
    // 只要不在这里写 Print(5); 编译器就不会去检查上面的错误
    //Print(5); //放开注释会提示 error C2065: 'valu': undeclared identifier
    std::cout << "Compiler is happy because Print is never called." << std::endl;
    std::cin.get();
}

以上代码编译通过并且可以运行

例子4#

#include <iostream>
#include <string>
 

//编译器会根据你的模板“蓝图”,为每一个不同的参数生成一份全新的类定义。这个过程被称为模板实例化 (Template Instantiation)。
template<typename T,int N>
class Array
{
private:
	T m_Array[N];
public :
	int GetSize() const
	{
		return N;
	}
};
 
int main()
{
	//int size = 5;
	// 在原生 C++ 中,数组的大小必须是一个常量表
	// 达式 (Constant Expression),即在编译阶段就能确定的数值。
	//int a[size];//编译不通过

	Array<int,50> array1;
	Array<double,4> array2;
	std::cout << array1.GetSize() << std::endl;
	std::cout << array2.GetSize() << std::endl;

	std::cin.get();
}

Array<int,50>Array<double,4> 在编译阶段会被视为两个完全独立的类型。

编译器会生成类似的伪代码:

// --- 编译器生成的第一个类 ---
class Array_Int_50 {
private:
    int m_Array[50]; // T 被替换为 int, N 被替换为 50
public:
    int GetSize() const {
        return 50; 
    }
};

// --- 编译器生成的第二个类 ---
class Array_Double_4 {
private:
    double m_Array[4]; // T 被替换为 double, N 被替换为 4
public:
    int GetSize() const {
        return 4;
    }
};

小结#

  • template,模板,在c++中属于元编程(meta programming)
  • 我们并不是在编写代码在运行时的行为,而是在编写编译器在编译时实际要做的事情
  • 模板在日志系统很有用,要记录的类型应有尽有
  • 用模板实现自动化,让编译器根据你写的规则编写代码
  • 不要滥用