C++详解非类型模板参数Nontype与Template及Parameters的使用
目录
- 非类型类模板参数
- 非类型函数模板参数
- 非类型模板参数的限制
- 非类型模板参数 auto
非类型类模板参数
前一章使用的例子 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&
到此这篇关于C++详解非类型模板参数Nontype与Template及Parameters的使用的文章就介绍到这了,更多相关C++非类型模板参数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!