非类型模板参数 Nontype Template Parameters
除了类型可以作为模板参数,普通值也可以作为模板函数,即非类型模板参数(Nontype Template Parameters)。前一章使用的例子使用的是标准库中的容器管理元素,也可以使用固定大小的 ,它的优势是内存管理开销更小,数组的大小可以交给用户指定。使用该模板需要同时指定类型和个数。用于指定的大小。非类型模板参数也可以有默认值。非类型函数模板参数也可以为函数定义非类型模板参数。也可以指定一个
非类型模板参数 Nontype Template Parameters
除了类型可以作为模板参数,普通值也可以作为模板函数,即非类型模板参数(Nontype Template Parameters)。
非类型类模板参数
前一章使用的例子 Stack
使用的是标准库中的容器管理元素,也可以使用固定大小的 std::array
,它的优势是内存管理开销更小,数组的大小可以交给用户指定。
#include <array>
#include <cassert>
template<typename T, std::size_t Maxsize>
class Stack {
private:
std::array<T,Maxsize> elems; // elements
std::size_t numElems; // current number of elements
public:
Stack(); // constructor
void push(T const& elem); // push element
void pop(); // pop element
T const& top() const; // return top element
bool empty() const { // return whether the stack is empty
return numElems == 0;
}
std::size_t size() const { // return current number of elements
return numElems;
}
};
template<typename T, std::size_t Maxsize>
Stack<T,Maxsize>::Stack ()
: numElems(0) // start with no elements
{
// nothing else to do
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
assert(numElems < Maxsize);
elems[numElems] = elem; // append element
++numElems; // increment number of elements
}
template<typename T, std::size_t Maxsize>
void Stack<T,Maxsize>::pop ()
{
assert(!elems.empty());
--numElems; // decrement number of elements
}
template<typename T, std::size_t Maxsize>
T const& Stack<T,Maxsize>::top () const
{
assert(!elems.empty());
return elems[numElems-1]; // return last element
}
int main()
{
Stack<int,20> int20Stack; // stack of up to 20 ints
Stack<int,40> int40Stack; // stack of up to 40 ints
Stack<std::string,40> stringStack; // stack of up to 40 strings
// manipulate stack of up to 20 ints
int20Stack.push(7);
std::cout << int20Stack.top() << '\n';
int20Stack.pop();
// manipulate stack of up to 40 strings
stringStack.push("hello");
std::cout << stringStack.top() << '\n';
stringStack.pop();
}
使用该模板需要同时指定类型和个数。 Maxsize
用于指定 std::array
的大小。非类型模板参数也可以有默认值。
template<typename T = int, std::size_t Maxsize = 100>
class Stack {
...
};
非类型函数模板参数
也可以为函数定义非类型模板参数。
template<int Val, typename T>
T addValue (T x)
{
return x + Val;
}
std::transform (source.begin(), source.end(), // start and end of source
dest.begin(), // start of destination
addValue<5,int>); // operation
也可以指定一个模板参数,由该参数之前的参数推断出其类型。
template<auto Val, typename T = decltype(Val)>
T foo();
或者保证传值的类型和指定的类型相同。
template<typename T, T Val = T{}>
T bar();
非类型模板参数的限制
需要注意的是,非类型模板参数有一定的限制。一般地,非类型模板参数可以是整形(包括枚举)、指向对象/函数/成员的指针、对象/函数的左值引用或空指针类型 std::nullptr_t
。
浮点数和类类型对象不可以作为非类型模板参数。
template<double VAT> // ERROR: floating-point values are not
double process (double v) // allowed as template parameters
{
return v * VAT;
}
template<std::string name> // ERROR: class-type objects are not
class MyClass { // allowed as template parameters
...
};
当使用指针或引用作为非类型模板参数时,不能用字符串字面值、临时对象、数据成员或其他子对象作模板实参。
template<char const* name>
class MyClass {
...
};
MyClass<"hello"> x; // ERROR: string literal "hello" not allowed
C++版本逐渐放宽了限制。C+11 之前,对象必须有外部链接;C++17 之前对象必须有外部或内部链接;C++17 放开了此限制。
extern char const s03[] = "hi"; // external linkage
char const s11[] = "hi"; // internal linkage
int main()
{
MyClass<s03> m03; // OK (all versions)
MyClass<s11> m11; // OK since C++11
static char const s17[] = "hi"; // no linkage
MyClass<s17> m17; // OK since C++17
}
非类型模板参数的实参可能是任何编译期表达式。
template<int I, bool B>
class C;
...
C<sizeof(int) + 4, sizeof(int)==4> c;
当表达式中使用了大于号,需要将整个表达式用小括号括起来。
C<42, sizeof(int) > 4> c; // ERROR: first > ends the template argument list
C<42, (sizeof(int) > 4)> c; // OK
非类型模板参数 auto
从 C++17 开始, 可以将非类型模板参数定义为 auto
,以接收任何允许作为非类型模板参数的类型。
#include <array>
#include <cassert>
template<typename T, auto Maxsize>
class Stack {
public:
using size_type = decltype(Maxsize);
private:
std::array<T,Maxsize> elems; // elements
size_type numElems; // current number of elements
public:
Stack(); // constructor
void push(T const& elem); // push element
void pop(); // pop element
T const& top() const; // return top element
bool empty() const { // return whether the stack is empty
return numElems == 0;
}
size_type size() const { // return current number of elements
return numElems;
}
};
// constructor
template<typename T, auto Maxsize>
Stack<T,Maxsize>::Stack ()
: numElems(0) // start with no elements
{
// nothing else to do
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::push (T const& elem)
{
assert(numElems < Maxsize);
elems[numElems] = elem; // append element
++numElems; // increment number of elements
}
template<typename T, auto Maxsize>
void Stack<T,Maxsize>::pop ()
{
assert(!elems.empty());
--numElems; // decrement number of elements
}
template<typename T, auto Maxsize>
T const& Stack<T,Maxsize>::top () const
{
assert(!elems.empty());
return elems[numElems-1]; // return last element
}
从 C++14 开始,已经支持使用 auto 作为函数返回值。因此成员函数 size()
可以简写为:
auto size() const { // return current number of elements
return numElems;
}
上述模板的使用:
int main()
{
Stack<int,20u> int20Stack; // stack of up to 20 ints
Stack<std::string, 40> stringStack; // stack of up to 40 strings
// manipulate stack of up to 20 ints
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';
}
}
前面说过,非类型模板参数 auto
接收任何允许作为非类型模板参数的类型。
#include <iostream>
template<auto T> // take value of any possible nontype parameter (since C++17)
class Message {
public:
void print() {
std::cout << T << '\n';
}
};
int main()
{
Message<42> msg1;
msg1.print(); // initialize with int 42 and print that value:42
static char const s[] = "hello";
Message<s> msg2; // initialize with char const[6] "hello"
msg2.print(); // and print that value:hello
}
非类型模板 auto
的参数仍不能是浮点数。
Stack<int,3.14> sd; // ERROR: Floating-point nontype argument
使用 decltype(auto)
指定非类型模板参数的类型也是可以的。
template<decltype(auto) N>
class C {
...
};
int i;
C<(i)> x; // N is int&
参考
- http://www.tmplbook.com
更多推荐
所有评论(0)