C++模版与泛型编程
模版是C++泛型编程的基础,泛型编程所依赖的是编译时多态性或参数式多态性。
定义模版
函数模版
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。
当我们调用一个函数模版时,编译器用函数实参来为我们推断模版实参。
当我们实例化出模版的一个特定版本时,编译器才会生成代码。
需要注意,当我们希望通知编译器一个名字表示类型时,必须使用关键字typename,而不能使用class。
template <typename T>
void test(const T& t)
{
// 此时p被声明为指针,而不是X * X
typename T::value_type * p;
}
类模版
template <typename T> class Blob
{
public:
typedef T value_type;
//std::vector<T>这个模板类中的size_type嵌套类型定义了一个叫做size type的别名。也可以使用typedef来引用实例化的类
typedef typename std::vector<T>::size_type, size type;
...
}
函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须在程序中显式地指定。即函数模板允许隐式调用和显式调用而类模板只能显示调用。在使用时类模板必须加,而函数模板不必。
template <template T1, template T2, template T3>
T3 alternative_sum(T2, T1);
//错误:不能推断前几个模板参数
auto val3 = alternative_sum<long long>(i, lng);
//正确:显示地指定了所有三个参数
auto val2 = alternative_sum<long long, int, long>(i, lng);
对于上述模板类型参数已经显示指定了得函数实参,可以进行正常的类型转换。
// 1和2被转换成long
alternative_sum<long, long, long long>(1, 2);
友元与类模板
如果一个类模板包含一个非模板友元,则友元被授权可以访问所以模板实例。如果友元自身是模板,类可以授权给所有友元模板实例,也可以只授权给特定实例。
template <typename T> class testfriend;
template <typename T>
class test
{
friend class testfriend<T>;
private:
int m_count;
};
template <typename T>
class testfriend
{
public:
int get_count(const test<T>& t) { return t.m_count; }
};
test<int> t;
testfriend<int> tf;
// 正确
tf.get_count(t);
testfriend<double> tf1;
// 错误,必须相同类型
tf1.get_count(t);
类模板的static成员
与任何其他类相同,类模板也可以声明static成员
template <typename T>
class Foo
{
public:
static std::size_t count() {return ctr;}
private:
static std::size_t ctr;
};
// 以下两个是不同的静态对象
Foo<string> fs;
Foo<int> fi;
// 对象共享
Foo<int> fi2, fi3;
模版实参推断和引用
当一个函数参数是模板类型参数的一个普通(左值)引用时(T&),绑定规则告诉我们,只能传递给它一个左值(如,一个变量或一个返回引用类型的表达式)。实参可以是const类型,则T将被推断为const类型。如果实参类型是const T&,则实参可以是右值,T的类型推断结果不会是一个const类型。
// 实参必须是一个左值
template <typename T>
void f1(T& t)
{
}
// 可以接受右值
template <typename T>
void f2(const T& t)
{
}
int i = 0;
const int j = 0;
f1(i); // 正确
f1(5); // 错误
f1(j); // 正确,T是const int
f2(i); // 正确
f2(5); // 正确
f2(j); // 正确,T是int
注意下面这个例子,
template <typename T>
void test(T&& t)
{
}
test(1); // 正确 T是int
int i = 0;
test(i); // 看起来i是左值,好像不合法,但是T被推断为int&,其实test被折叠成左值引用
引用折叠规则:
T& &(引用的引用)、T& &&(右值引用的引用)、T&& &(引用的右值引用)均折叠为T &。
T&& &&(右值引用的右值引用)折叠为T &&。
上面的类型别名和函数模板均触发了引用折叠。引用折叠是std::move、std::forward等的工作基础。
未完待续
多重定义?
何时必须使用typedefine?
理解std::move、std::forward,理解右值引用