3.2 虽旧犹新的语言特性
非类型模板参数
1.除了类型参数之外,我们也可以为template使用nontype paramatter.
2.非类型参数看作是template类型的一部分
bitset<32> flags32;
bitset<50> flags50;
// 这两个看作是两个不同类型的template
模板参数默认值
class template 可以拥有默认参数
function template也可以拥有默认参数
// 类模板
template <typename T1, typename T2 = int>
class TmpClass1 {};// no default argument for 'T2',感觉有点像函数的默认参数,一个参数有了默认值,那么这个参数后面的参数都必须有默认值
// 通过查阅资料:类模板的默认模板参数从右往左开始指定
// template <typename T1 = double, typename T2>
// class TmpClass2 {};// 函数模板
// 这两个都能编译通过
template <typename T1 = double, typename T2>
void func(T1 arg1, T2 arg2) {}template <typename T1, typename T2 = double>
void func1(T1 arg1, T2 arg2) {}
关键字typename
关键值typename用来指明后面紧跟的是个类型
注意事项:C++的一般性规则是,template内的任何标识符都被视为一个value,除非它加上typename
#include <iostream>class Q
{
public:using SubType = int;
};template <typename T>
class MyClass
{// 这样写编译器会直接提示需要在前面加上typename// T::SubType *ptr;typename T::SubType *ptr;public:MyClass() {}~MyClass() {}
};int main(int argc, char const *argv[])
{/* code */MyClass<Q> myclassq;return 0;
}
成员模板
class的成员函数可以是一个template。然而member template不可以是virtual
通常用来支持class template内的成员之间的自动类型转化。
#include <iostream>
// 基本用法
class MyClass
{public:MyClass() {}~MyClass() = default;template<typename T>void f(T a){// 这里面的操作是T这个类型支持的。;//}
};// 常用用法
template<typename T>
class MyClass1
{private:T value;public:template <typename X>void assign(const MyClass1<X>&x){value = x.getValue();}T getValue() const {return value;}
};void f()
{MyClass1<double> d;MyClass1<int> i;d.assign(d);d.assign(i);
}// 特殊情况: member template 构造函数
template <typename T>
class MyClass2
{
public:// 没有这个会报错:error: no matching function for call to 'MyClass2<double>::MyClass2()'MyClass2() {}template <typename U>MyClass2(const MyClass2<U> &x){std::cout << "MyClass(const MyClass<U>&x)" << std::endl;}
};void f1()
{MyClass2<double> xd;MyClass2<double> xd2(xd);MyClass2<int> xi(xd);// 输出结果:只有一次MyClass(const MyClass<U>&x)// 通过查阅资料:发现当xd2与xd类型相同时,通过隐式生成的copy构造函数完成。类型不同的时候才通过我们写的那个template
}int main(int argc, char const *argv[])
{f();f1();return 0;
}
嵌套式的class template
template <typename T>
class MyClass
{template<typename T2>class NestedClass {};
};
3.2.1 基础类型的明确初始化
知识点:
“一个明确的构造函数调用,但不给实参”这样的语法,基础类型会被设定初值0
如果一个template强迫设置初值为0,其值就是所谓的zero initialized,否则就是default initialized
int i1; // undefined value
int i2 = int(); // 0
int i3{}; // 0
利用这一个特性
template <typename T>
void f()
{T x = T();std::cout << x << std::endl;
}
3.2.2 main()定义式
// 这个是我这补全工具自动补全生成的,没有报错。
int main(int argc, char const *argv[])
{
}// main的合法定义式
int main()
{}int main(int argc, char **argv)
{}int main(int argc, char *argv[])
{}
// 通常情况下,会写一个return 0,但是不是必须。
// 可以使用exit()/quick_exit()/terminate()