非类型的类模板参数

先让我们用标准库中的std::array定义一个Stack:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
template <typename T, std::size_t Maxsize>
class Stack
{
private:
std::array<T, Maxsize> stack;
std::size_t numElems = 0;

public:
Stack() = default;
~Stack() = default;

void push(const T &value)
{
stack[numElems++] = value;
}

void pop()
{
assert(numElems > 0);
--numElems;
}

const T &top() const
{
assert(numElems > 0);
return stack[numElems - 1];
}

bool empty() const
{
return numElems == 0;
}

std::size_t size() const
{
return numElems;
}
};

注意到第二个模板参数并非我们之前见到过的类型,而是一个int参数,用来指定存放栈元素的内部数组大小。

如果我们要使用这个模板类,必须同时指定元素类型和最大容量:

1
2
3
4
5
6
7
8
9
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);

Stack<int, 10> stack;

return 0;
}

这就是非类型模板参数的使用方法了,同类型参数一样,我们也可以为它指定默认值:

1
2
template <typename T = int, std::size_t Maxsize = 100>
class Stack;

非类型的函数模板参数

函数模板也可以使用非类型参数:

1
2
3
4
5
template <int Val, typename T>
T addValue(T t)
{
return t + Val;
}

使用方法:我们用标准库中的std::transform来为数组中的某一段元素都增加某一个值:

1
2
std::vector<int> a{1, 2, 3, 4, 5};
std::transform(a.begin(), a.end(), a.begin(), addValue<5, int>);

std::transform的最后一个参数接收了一个实例化的函数的指针。

其他用法:

1
2
3
4
5
template <auto Val, typename T = decltype(Val)> // 默认T和传入参数一致
T foo();

template <typename T, T Val = T{}> // 默认Val类型和T保持一致
T foo2();

非类型模板参数的限制

一般来说,非类型模板参数只能是:

1.int类型常量和enum类型常量

2.对象/函数/成员的指针

3.对象或函数的左值引用

4.std::nullptr_t

auto模板参数类型

使用auto可以将参数传入数据设定成非类型模板参数所允许的任何类型

1
2
template <typename T, auto Maxsize>
class Stack;

显然这个例子不太好,毕竟Maxsize一定要是整型,但也足以让我们明确它的用法。

从C++14开始,auto也可用作推到返回类型:

1
2
3
4
template <typename T1, typename T2>
auto sum(T1 a, T2 b) {
return a + b;
}