非类型模板参数(参考《C++ Templates 英文版第二版》)
Chapter 3
3.1 非类型类模板参数
与前几章的简单例子不同,你也可以通过std::array 实例化一个固定大小的栈,这样做的优点在于内存管理,
#include <array>
#include <cassert>
template<typename T, std::size_t Maxsize>
class Stack {
private:
std::array<T,Maxsize> elems;
std::size_t numElems;
public:
Stack();
void push(T const& elem);
void pop();
T const& top() const;
bool empty() const {
return numElems == 0;
}
std::size_t size() const {
return numElems;
}
};
template<typename T, std::size_t Maxsize>
Stack<T,Maxsize>::Stack ()
: numElems(0)
{
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
assert(numElems < Maxsize);
elems[numElems] = elem;
++numElems;
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::pop ()
{
assert(!elems.empty());
--numElems;
}
template<typename T, std::size_t Maxsize>
T const& Stack<T,Maxsize>::top () const
{
assert(!elems.empty());
return elems[numElems-1];
}
运行一下:
#include "stacknontype.hpp"
#include <iostream>
#include <string>
int main()
{
Stack<int, 20> int20Stack;
Stack<int, 40> int40Stack;
Stack<std::string, 40> stringStack;
int20Stack.push(7);
std::cout << int20Stack.top() << '\n';
int20Stack.pop();
stringStack.push("hello");
std::cout << stringStack.top() << '\n';
stringStack.pop();
}
你也可以设置默认参数
template<typename T, std::size_t Maxsize = 100> ,但最好不要这样做,因为栈大小最好还是有程序员自己控制
3.2 非类型函数模板参数
你也可以为函数定义非类型模板参数
template<int val,typename T>
int addval(T a)
{
return a + val;
}
int main()
{
std::cout << addval<10>(10) << std::endl;
}
这种函数是很有用的,可以将它作为参数.
例如,如果你可以使用STL,你可以传入一个函数模板的实例,使集合中的每个数加一个值
std::vector<int> source{ 1,4,5 };
std::vector<int> dest{0,0,0};
std::transform(source.begin(), source.end(), dest.begin(), addval<10,int>);
for (auto value : dest)
{
std::cout << value << std::endl;
}
如果你制定非类型函数模板参数是int 那么就无法使用其他类型,是有什么方法可以做到自动推导其他类型呢?
其实,你可以指定一个根据之前的模板参数推断出来的模板参数
template<auto val,typename T = decltype(val)>
T addval(T a)
{
return a + val;
}
3.3 非类型模板参数的限制
非类型模板参数有一些限制,例如,它们只能是整数,(对象,函数,成员)指针,(对象,函数,成员)引用,std::nullptr
浮点数指针和类对象不可以作为非类型模板参数
3.4 模板参数类型auto
C++17 ,你可以定义一个非类型模板模板参数去普遍的接收任何类型,使用这个特性,你可以定义一个更加一般的固定大小栈
#include <array>
#include <cassert>
template<typename T, auto Maxsize>
class Stack {
public:
using size_type = decltype(Maxsize);
private:
std::array<T,Maxsize> elems;
size_type numElems;
public:
Stack();
void push(T const& elem);
void pop();
T const& top() const;
bool empty() const {
return numElems == 0;
}
size_type size() const {
return numElems;
}
};
template<typename T, auto Maxsize>
Stack<T,Maxsize>::Stack ()
: numElems(0)
{
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
assert(numElems < Maxsize);
elems[numElems] = elem;
++numElems;
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::pop ()
{
assert(!elems.empty());
--numElems;
}
template<typename T, auto Maxsize>
T const& Stack<T,Maxsize>::top () const
{
assert(!elems.empty());
return elems[numElems-1];
}
运行:
Stack<int, 20u> int20Stack;
Stack<std::string, 40> stringStack;
int20Stack.push(7);
std::cout << int20Stack.top() << '\n';
auto size1 = int20Stack.size();
stringStack.push("hello");
std::cout << stringStack.top() << '\n';
auto size2 = stringStack.size();
if (!std::is_same_v<decltype(size1), decltype(size2)>) {
std::cout << "size types differ" << '\n';
}
|