1、非类型模板参数
前面两篇文章中举的例子中模板参数都是类型,但是在C++模板编程中,模板参数不仅仅可以是类型,也可以是非类型。这里举个简单的例子:
template <int N>
void print() {
std::cout << N << std::endl;
}
int main() {
MyTemplate::print<3>(); // 3
}
例中print的模板参数是一个int,并不是我们之前所使用的<typename T>
。
非类型模板参数可以是以下几种类型:
- 整型或者枚举
- 指向对象或者函数的指针
- 引用类型
- 指向成员的指针
- std::nullptr_t
template<int SIZE>
class A{ /*...*/ }; // 整型非类型模板参数
template<char *p>
class X { /*...*/ }; // 指针非类型模板参数,全局/静态变量指针
template<int ( *func)(int)>
struct Y { /*...*/ }; // 函数指针非类型模板参数
template<int& R>
struct Z { /*...*/ }; // 引用非类型模板参数,需引用全局/静态变量
struct C { int m; };
template<int C::* M>
struct Q { /*...*/ }; // 指向成员的指针非类型模板参数
非常注意的是:非类型模板参数的值必须在编译期确定。
编译期可以确定的值或变量有如下几个:
- 字面量:例如整数、字符、布尔值等,浮点型并不属于编译器可以确定的;
template<int N>
struct Constant {
static const int value = N;
};
constexpr int a = Constant<5>::value;
- 全部变量或静态变量的地址或引用
char g_s[] = "abc";
template<char *p>
struct Test() {
void print { std::cout << p << std::endl; }
};
Test<g_s> obj;
- 全局或静态函数的地址(函数指针)
void print() {
std::cout << "Hello Function" << std::endl;
}
template<void (*func)()>
struct TestFunction {
void exec() { func(); }
};
TestFunction<print> testFunction;
testFunction.exec();
- 枚举enum
enum class Color { Red, Blue, Green };
template<Color color>
struct Test {
};
Test<Color::Red> obj; // 使用Color::Red模板实例化Test
- 指向成员的指针
struct Example { int value; };
template<int Example::*ptr>
struct Test2 {
void print(Example& e) {
std::cout << e.*ptr << std::endl;
}
};
Example e;
e.value = 10;
Test2<&Example::value> obj2;
obj2.print(e);
成员指针可能会比较让人疑惑,Example::*ptr
表示Example中的某个成员,int
限定了这个成员的类型,更多的成员指针内容可参考:
这里有一点要注意,非类型模板参数如果是指向成员的指针,指向的类型可以是任意类型:
struct Example {
int value;
People p;
};
template<People Example::*ptr>
struct Test2 {
void print(Example& e) {
std::cout << e.*ptr << std::endl;
}
};
非类型模板参数就暂时先了解到这,我们需要记住的是,C++模板参数不仅仅可以是类型,还可以是编译期可以确定的值或者是变量。