C++模板新手详解(下)
C++模板新手详解(上)-CSDN博客
编写通用模板代码重要技巧
来源:vector 类模拟实现学习vector类
const T& val = T()
以下是vector类模拟实现中 resize() 部分代码:
/* resize */
// val不能给0,因为不知道T的类型,所以给一个T的缺省值
void resize(size_t n, const T& val = T())
{
// 缩小size
if (n < size())
_finish = _start + n;
else // 增大size
{
// 假如需要扩容
if (n > capacity())
{
reserve(n);
}
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
const T& val = T() 的含义
这行代码实际上包含了三个关键概念:
1. T() - 值初始化
-
T()表示创建一个类型为T的临时对象,并使用值初始化 -
对于内置类型(如
int,double,指针等),T()会将其初始化为零值:-
int()→0 -
double()→0.0 -
char()→'\0' -
指针()→nullptr
-
-
对于类类型,
T()会调用默认构造函数
2. const T& - 常量引用
-
使用常量引用可以避免不必要的对象拷贝
-
常量引用可以绑定到临时对象(如
T()创建的对象)
3. = T() - 默认参数
-
这是函数的默认参数,如果调用时不提供这个参数,就使用
T()作为默认值
为什么要这样设计?
通用性
由于 vector 是模板类,需要处理任意类型 T:
-
如果是
vector<int>,T()就是0 -
如果是
vector<string>,T()就是空字符串 -
如果是
vector<MyClass>,T()会调用MyClass的默认构造函数
示例代码:
vector<int> v1;
v1.resize(10); // 使用默认值 int() = 0,所有新元素初始化为0
vector<double> v2;
v2.resize(5); // 使用默认值 double() = 0.0
vector<string> v3;
v3.resize(3); // 使用默认值 string() = 空字符串// 也可以显式指定值
v1.resize(15, 100); // 新元素初始化为100
v3.resize(5, "hello");// 新元素初始化为"hello"
为什么不能直接用 0?
为什么不能写成 void resize(size_t n, const T& val = 0)?
这是因为:
-
类型不匹配:如果
T是string,0不是有效的字符串值 -
缺乏通用性:不是所有类型都能从
0构造 -
编译错误:对于不能从
0转换的类型,代码无法编译
而 T() 为每种类型提供了最合适的"零值"或默认值,保证了代码的通用性和类型安全。
总结
const T& val = T() 是一个巧妙的设计:
-
通用:适用于任何类型
T -
高效:使用引用避免拷贝
-
安全:提供类型安全的默认值
-
灵活:允许调用者提供自定义值,也有合理的默认值
这种写法在STL和现代C++库中非常常见,是编写通用模板代码的重要技巧之一。
非类型模板参数
模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
/* 非类型模板参数 */
/* <类型参数, 非类型模板参数> */
template<class T, int N>
class Array
{
public:
// ...
private:
T _a[N];
};
int main()
{
Array<int, 100> a1; // 100 个 int 类型空间的静态数组
Array<int, 1000> a2; // 1000 个 int 类型空间的静态数组
return 0;
}
注意:
-
浮点数、类对象以及字符串是不允许作为非类型模板参数的。
-
非类型的模板参数必须在编译期就能确认结果。
2. 模板的特化
template<class T1, class T2>
class Data
{
public:
Data() { cout << "原模板类:Data<T1,T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
// 全特化 全部参数都特化
template<>
class Data<int, char>
{
public:
Data() { cout << "全特化:Data<int,char>" << endl; }
private:
};
// 偏特化
template<class T2>
class Data<int, T2>
{
public:
Data() { cout << "偏特化:Data<int,T2>" << endl; }
private:
};
// 偏特化
template<class T1, class T2>
class Data<T1*, T2*>
{
public:
Data() { cout << "偏特化:Data<T1*,T2*>" << endl; }
private:
};
// 偏特化
template<class T1, class T2>
class Data<T1&, T2&>
{
public:
Data() { cout << "偏特化:Data<T1&,T2&>" << endl; }
private:
};
示例使用:
void test2()
{
// 可以特化很多个模板
Data<int, int> d1;
Data<int, double> d2;
Data<int, char> d3;
Data<char, char> d4;
Data<char*, char*> d5;
Data<char&, char&> d6;
}
3. 分离编译
项目工程中一般将函数或者类的声明放到.h,将函数或者类的定义放到.cpp,这么做是为了方便查看和维护。
但是,模板不能分离编译。
同样是分离编译,普通函数/类可以,函数模板/类模板为什么不行?


3.3 解决方法
1. 将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种。
2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。
【分离编译扩展阅读】 http://blog.csdn.net/pongba/article/details/19130
更多推荐



所有评论(0)