分离编译
一个程序由若干个源文件共同实现,每个源文件单独编译生成目标文件,最后将所有的目标文件链接起来形成单一可执行文件的过程称之为分离编译模式。模板不支持分离编译
编译器报的这种错误属于链接性错误,也就是当程序预处理、编译、汇编、链接,在链接时出现了错误。而一般出现链接错误,通常是因为,声明了一个函数,但是没有写实现体。因此,当程序在链接时,从符号表中只找到了函数名,找不到具体函数实现的地址,因此编译器会报出这样的错误!
那么问题又来了:为什么当模板在分离编译时会报出这样的错误呢?
模板需要两次编译:
第一次编译是在实例化之前:用来分析基本的语法错误,
第二次编译是在实例化之后,当把这个类型替换之后,判断有没有语法错误。
重点就是在这里!!!!!!!!我们模板代码的实现体在一个文件里,而实例化模板的测试代码在另一个文件里,编译器编译一个文件时并不知道另一个文件的存在,因此,模板代码就没有进行实例化,编译器自然不会为其生成代码,因此会抛出一个链接错误!
因此如果需要对上方所出现的问题进行解决的话,将声明和定义放到一个文件“xxx.h”(头文件)里面。
类型萃取
相关知识:
C++ typeid运算符:获取类型信息
typeid 运算符用来获取一个表达式的类型信息。类型信息对于编程语言非常重要,它描述了数据的各种属性:
- 对于基本类型(int、float 等C++内置类型)的数据,类型信息所包含的内容比较简单,主要是指数据的类型。
- 对于类类型的数据(也就是对象),类型信息是指对象所属的类、所包含的成员、所在的继承关系等。
类型信息是创建数据的模板,数据占用多大内存、能进行什么样的操作、该如何操作等,这些都由它的类型信息决定。
typeid 的操作对象既可以是表达式,也可以是数据类型,下面是它的两种使用方法:
typeid( dataType )
typeid( expression )
dataType 是数据类型,expression 是表达式,这和 sizeof 运算符非常类似,只不过 sizeof 有时候可以省略括号( )
,而 typeid 必须带上括号。
#include <iostream>
#include <typeinfo>
using namespace std;
int main() {
cout << typeid(int).name() << endl; // i
cout << typeid(1.0).name() << endl; // d
return 0;
}
问题场景:
判断给定类型是否为内建类型
方案:继承+类模板特化
#include <iostream>
using namespace std;
//TODO BEGIN
class yes {
public:
static char* flag;
};
char* yes::flag = (char *)"yes";
class no {
public:
static char* flag;
};
char* no::flag = (char *)"no";
class A {};
class B {};
template<typename T>
class type_trais : public no {};
template<>
class type_trais<int> : public yes {};
template<>
class type_trais<double> : public yes {};
template<>
class type_trais<int *> : public yes {};
//TODO END
#define TEST(type) \
cout << #type << " : " << type_trais<type>::flag << endl;
int main() {
TEST(int);
TEST(A);
TEST(double);
TEST(B);
TEST(string);
TEST(int *);
TEST(string *);
return 0;
}
/*
int : no
A : yes
double : no
B : yes
string : yes
string * : no
*/
变参函数模板
//类似函数模板的偏特化,实则是一种重载形式
//函数模板没有偏特化
//注意:而函数重载本质是函数,在使用之前必须有定义
template<typename T> //递归出口
void Print(T a) {
cout << a << endl;
return ;
}
template<typename T, typename ...ARGS>
void Print(T a, ARGS... args) {
cout << a << " | ";
Print(args...); //递归入口
return ;
}
int main() {
Print("hello world!", 666, 1.1, '$');
Print("hello world!", 666, 1.1);
Print("hello world!", 666);
Print("hello world!");
return 0;
}
/*
hello world! | 666 | 1.1 | $
hello world! | 666 | 1.1
hello world! | 666
hello world!
*/