4_1_函数模板.cpp
#include "hjcommon.hpp"
using namespace std;
HJ_NS_USING
HJ_NS_BEGIN
// 类型参数的函数模板,类型 T 可以用任意字符或字符串代替
template<typename T> // 可以使用typename或class,甚至可以两者混用。多个模板参数用 , 隔开。模板参数可以传递类型参数,也可以传递非类型参数(值)
T add(T a, T b)
{
return a+b;
}
// 非类型参数的函数模板
template<int a, int b> // 这种非类型模板参数的值,或者是用户提供的,或者是编译器提供的。 这些值必须是常量表达式
int jian()
{
return a-b;
}
// 类型参数与非类型参数的函数模板
template<typename T, int a, int b>
static inline int jia(T t) // 模板函数可以内联,也可以 static
{
return (int)t + a + b;
}
// 自动推导
template<unsigned L1, unsigned L2> // unsigned L1表示无符号的整型,可以不把数据类型写全,编译器会自动推导。。
int cmp(const char(&str1)[L1], const char(&str2)[L2]) // const char(&str1)[5] = "some"; // 字符数组的const左值引用
{
return strcmp(str1, str2); // strcmp 两个字符串相同返回0,否则非0
}
HJ_NS_END
int main_2_4_1(int argc, char *argv[])
{
// 类型参数的模板函数
int ret = add(2, 3); // 可以,编译器能自动类型推导
// ret = add(2, 3.f); // 不可以,自动推导失败
ret = add<>(2, 3); // 可以
ret = add<int>(2, 3); // 可以
// 非类型参数的模板函数
ret = jian<2, 3>(); // 显式的提供模板参数, <> 中给定
// 类型参数与非类型参数的函数模板
ret = jia<double, 2, 3>(1);
// 自动推导
ret = cmp("some", "som"); // 可以,参数的字符串长度编译器会自动推导
ret = cmp<5, 4>("some", "som"); // 可以,手动给定字符串的长度
// ret = cmp<6, 4>("some", "som"); // 不可以,字符串长度不对
cout << ret << endl;
// 当定义模板的时候,编译器不会生成模板的代码,只有有用户调用的时候,编译器才会生成对应的模板代码, 所以模板一般写在头文件里,而且写在头文件里也不会造成重复定义
return 0;
}
4_2_类模板.cpp
#include "hjcommon.hpp"
using namespace std;
HJ_NS_USING
HJ_NS_BEGIN
template<typename T=int, int size=10> // 类模板,c++11中模板函数与模板类的类型参数与非类型参数都可以给默认值的,c++98只允许模板类可以有默认参数
class myvector
{
public:
typedef T* myiterator; // 这样定义的,myiterator 称为类的类型成员,当类的类型成员在类的外部或类的函数实现处使用,那么需要用 类名<>::类的类型成员 格式使用
myvector(); // 类模板中所有函数如果在头文件声明时就写函数体,可以按照正常函数的写法
myvector& operator=(const myvector &vec); // 函数声明时类名后加不加 <T, size> 都可以
myiterator mybegin(); // 模板类的成员,只有用户实例化后有使用,才会被编译器把该成员加进模板类中
myiterator myend();
void func() { cout << "func." << endl; }
};
template<typename T, int size> // 这里size可以不用带上默认值
myvector<T, size>::myvector() // 模板类的函数实现如果另写在源文件中,那么需要加上模板类的模板,同时类名后需要带上 <所有模板参数>
{
cout << "no arg constructor. size=" << size << endl;
}
template<typename T, int size>
myvector<T, size>& myvector<T, size>::operator=(const myvector &vec) // 参数 const myvector &vec 加不加 <T, size> 都可以
{
}
template<typename T, int size>
// 类的类型成员在函数声明外的函数实现处使用时,需要给函数加上 typename 修饰,否则编译报错
// 当函数(包括全局函数、类成员函数)的返回值类型为类的类型成员时,需要用 typenmae 告诉编译器这里是类的类型成员。这里的typename不能用class取代
// 总结:类的类型成员在使用的时候,前面都应该加上 typename 修饰
typename myvector<T, size>::myiterator myvector<T, size>::mybegin()
{
}
HJ_NS_END
int main_2_4_2(int argc, char*argv[])
{
myvector<> vec1; // 可以,类模板使用时,必须带上<>,如果模板参数都有默认值,那么可不提供参数
myvector<float> vec2; // 可以
myvector<String, 100> ls; // 可以
ls.func();
// float、double、类类型 都不可以作为函数模板与类模板的非类型模板参数
return 0;
}
4_3.cpp
#include "hjcommon.hpp"
using namespace std;
HJ_NS_USING
HJ_NS_BEGIN
static int add(int a, int b)
{
int ret = a+b;
cout << "add. ret=" << ret << endl;
return ret;
}
class A
{
public:
A() { cout << "no arg constructor." << endl; }
A(const A &a) { cout << "copy constructor." << endl; }
int operator()(int a, int b) const
{
int ret = a+b;
cout << "operator(). ret=" << ret << endl;
return ret;
}
};
//template<typename T, typename F=int(*)(int, int)> // 另一种写法,模板参数默认值为函数指针类型
//static void testFunc(const T &a, const T &b, F funcPoint=add)
template<typename T, typename F=A> // 如果类类型为模板参数的默认值,那么模板函数的对应的形参也需要给默认值
static void testFunc(const T &a, const T &b, F funcPoint=F()) // 默认参数s
{
funcPoint(a, b); // 函数调用,这里表示类型F是一个函数指针类型,如果一个类重载了 operator() 运算符,那么该类也可以当作是函数指针的数据类型,该类的对象可称为可调用对象
}
HJ_NS_END
int main_2_4_3(int argc, char *argv[])
{
/* 打印:
add. ret=4
no arg constructor.
copy constructor.
operator(). ret=4
no arg constructor.
operator(). ret=4
no arg constructor.
operator(). ret=4
*/
testFunc(1, 3, add);
A a;
testFunc(1, 3, a);
testFunc(1, 3, A()); // 相比上句代码,这里不会再多调用一次拷贝构造函数,即少一个临时对象的产生
testFunc(1, 3); // 模板函数添加默认参数后,打印与上句代码一样
return 0;
}