C++程序员应了解的那些事(68)非类型模板参数
程序员文章站
2022-07-12 22:57:47
...
模板除了定义类型参数,我们还可以在模板定义非类型参数。
什么是非类型形参?顾名思义,就是表示一个固定类型的常量而不是一个类型。
※ 固定类型是有局限的,只有整形,指针和引用才能作为非类型形参;
※ 而且绑定到该形参的实参必须是常量表达式,即编译期就能确认结果。
非类型形参的局限:
1.浮点数不可以作为非类型形参,包括float,double。具体原因可能是历史因素,也许未来C++会支持浮点数;
2.类不可以作为非类型形参;
3.字符串不可以作为非类型形参;
4.整形,可转化为整形的类型都可以作为形参,比如int,char,long,unsigned,bool,short(enum声明的内部数据可以作为实参传递给int,但是一般不能当形参);
5.指向对象或函数的指针与引用(左值引用)可以作为形参。
非类型实参的局限:
1.实参必须是编译时常量表达式,不能使用非const的局部变量,局部对象地址及动态对象;
2.非const的全局指针,全局对象/全局变量(下面可能有个特例)都不是常量表达式;
3.由于形参的已经做了限定,字符串,浮点型即使是常量表达式也不可以作为非类型实参;
备注:常量表达式基本上是字面值以及const修饰的变量
*************************************************************************************************
<例1-整型>
const int r = 777;
template<int r>
void R()
{
cout << r << endl;
}
----------------------
int main()
{
R<666>();
R<r>();
const int r2 = 888;
R<r2>();
int r3 = 999; //局部变量
// R<r3>(); //错误:包含非静态存储持续时间的变量不能用作非类型参数(非const的局部变量)
}
<例2-指针>
char x1[] = "saaaa";//全局变量
char * x2 = "qweqeq";//全局变量
char * const x3 = "qweqeq";//全局变量 指针常量
(warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings] 修改为:const char *x2 = "qweqeq";)
template<typename T, char* x>
void X(T t)
{
cout << t << ", " << x << endl;
};
---------------------------------
int main()
{
X<int, x1>(3);
// X<int, x2>(4); // 错误: 应为编译时常量表达式
// X<int, x3>(5); // 错误:非const的全局指针! 涉及带有内部链接的对象的表达式不能用作非类型参数
char *x4 = "adsas";//局部变量,告警:ISO C++ forbids converting a string constant to 'char*'
// X<int, x4>(6);// 错误: 包含非静态存储持续时间的变量不能用作非类型参数
// X<int, "sdfsd">(7);//错误:字符串,浮点型即使是常量表达式也不可以作为非类型实参
}
<例3-整型>
template<int a>
void A(const char(&p)[a])
{
std::cout << "size : " << a << " " << p << std::endl;
}
-----------------------------------------
int main()
{
A("hello") ;
A<6>("hello");
}
<例4-引用>
struct Y {};
Y y;
template<const Y& b>
struct Z {};
int q = 1;
template<const int& q>
struct Q {};
--------------------
int main()
{
Z<y> z;
Q<q> q1;
}
<例5-数组指针>
int b[5] = {11,22,33,44};
template<int(&pa)[5]>
void B()
{
cout << pa[1] << endl;
};
---------------------------
int main()
{
B<b>(); // ok: no conversion
}
<例6-函数指针>
void f(int a)
{
cout << "a is "<<a << endl;
}
template<void(*pf)(int)>
void C()
{
pf(111);
};
----------------------------------
int main()
{
C<&f>(); // ok: overload resolution selects f(int)
}
<例7-模板 模板参数>
template<typename T> class N { public: int x; }; // primary template 通用模板
template<class T> class N<T*> { public: long x; }; // partial specialization 偏特化
template< template<typename> class V >
class M
{
public:
V<int> y; // uses the primary template
V<int*> z; // uses the partial specialization
};
------------------------------------
int main()
{
M<N> m = M<N>(); //显示地调用内建类型的缺省构造函数
cout << m.y.x << endl;
}
推荐阅读
-
程序员应了解的那些事(16)C语言中利用setjmp和longjmp做异常处理 / 不要在C++中使用setjmp和longjmp
-
C++程序员应了解的那些事(64)~ 指向 Data Member 的指针 <成员指针>
-
C++程序员应了解的那些事(68)非类型模板参数
-
C++程序员应了解的那些事(47)函数之 传入传出参数 / 默认参数
-
C++程序员应了解的那些事(36)Effective STL第6条:当心C++编译器中最烦人的分析机制 --- 调用构造函数被误认为是函数声明的问题
-
C++程序员应了解的那些事(63)STL内建函数对象、仿函数
-
C++程序员应了解的那些事(62)~ list::splice()函数详解
-
C++程序员应了解的那些事(94)之STL容器内存释放问题
-
C++程序员应了解的那些事(99)之 C++中的ODR法则
-
C++程序员应了解的那些事(18)C++11 通过key访问map容器:下标访问、at()、find、lower_bound&upper_bound、equal_range