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()