3.4 Template Parameter Type auto
3.4 模板参数类型auto
Since C++17, you can define a nontype template parameter to generically accept any type that is allowed for a nontype parameter. Using this feature, we can provide an even more generic stack class with fixed size:
从C++17开始,你可以定义一个非类型模板参数,该参数可普遍接受非类型模板参数允许的任意类型。利用这个特性,我们可以提供一个具有固定大小的、更通用的栈(stack)。
#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 isempty 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 }
By defining
按照定义
template<typename T, auto Maxsize> class Stack { … };
by using the placeholder type auto, you define Maxsize to be a value of a type not specified yet. It might be any type that is allowed to be a nontype template parameter type.
通过使用占位符类型auto,可以将Maxsize定义为尚未指定类型的值。它可以是非类型模板参数允许的任何类型。
Internally you can use both the value:
在模板内部,你可以利用这两个值
std::array<T, Maxsize> elems; //元素及其类型: using size_type = decltype(Maxsize);
which is then, for example, used as return type of the size() member function:
例如,将它作为size()成员函数的返回类型:
size_type size() const { //return current number of elements return numElems; }
Since C++14, you could also just use auto here as return type to let the compiler find out the return type:
从C++14开始,你也可以在此处使用auto作为返回类型,以便让编译器找出返回类型:
auto size() const { //return current number of elements return numElems; }
With this class declaration the type of the number of elements is defined by the type used for the number of elements, when using a stack:
通过这个类的声明,元素数量的类型(即size()的返回类型)是由使用栈时定义的元素数量的类型决定的。
#include <iostream> #include <string> #include "stackauto.hpp" 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() << ’ ’; auto size1 = int20Stack.size(); // manipulate stack of up to 40 strings stringStack.push("hello"); std::cout << stringStack.top() << ’ ’; auto size2 = stringStack.size(); if (!std::is_same_v<decltype(size1), decltype(size2)>) { std::cout << "size types differ" << ’ ’; } }
With
通过
Stack<int,20u> int20Stack; // stack of up to 20 ints
the internal size type is unsigned int, because 20u is passed.
内部的size为unsigned int类型,因为传入20u。
With
通过
Stack<std::string,40> stringStack; // stack of up to 40 strings
the internal size type is int, because 40 is passed. size() for the two stacks will have different return types, so that after
内部size的类型为int型,因为传入40。两个栈的size()函数具有不同的把返回类型,因此,当定义
auto size1 = int20Stack.size();
…
auto size2 = stringStack.size();
the types of size1 and size2 differ. By using the standard type trait std::is_same (see Section D.3.3 on page 726) and decltype, we can check that:
之后,size1和size2的类型是不同的。通过标准的类型萃取std::is_same(请参阅第726页的D3.3节)和decltype,我们可以检验这点:
if (!std::is_same<decltype(size1), decltype(size2)>::value) { std::cout << "size types differ" << ’ ’; }
Thus, the output will be:
因此,输出结果为:
size types differ
Since C++17, for traits returning values, you can also use the suffix _v and skip ::value (see Section 5.6 on page 83 for details):
从C++17开始,为了萃取返回值,你也可以使用后缀“_v“并省略”::value“(详细信息,请参阅第83页的5.6节)
if (!std::is_same_v<decltype(size1), decltype(size2)>) { std::cout << "size types differ" << ’ ’; }
Note that other constraints on the type of nontype template parameters remain in effect. Especially, the restrictions about possible types for nontype template arguments discussed in Section 3.3 on page 49 still apply. For example:
注意,对于非类型模板参数类型的其他约束仍然有效。特别是,第49页3.3节中讨论的关于非类型模板参数可能类型的限制仍然适用。
Stack<int, 3.14> sd; // ERROR: 浮点数不能作为非类型模板参数
And, because you can also pass strings as constant arrays (since C++17 even static locally declared; see Section 3.3 on page 49), the following is possible:
并且,因为你还可以将字符串作为常量数组进行传递(从C++17开始,甚至是局部静态声明,见第49页的3.3节),因此,可以出现以下的情况:
#include <iostream> template<auto T> // take value of any possible nontype parameter (since C++17) class Message { public: void print() { std::cout << T << ' '; } }; int main() { Message<42> msg1; msg1.print(); //用整数42初始化,并打印该值。 static char const s[] = "hello"; Message<s> msg2; // 用char const[6] "hello"初始化 msg2.print(); // 并打印该值 }
Note also that even template<decltype(auto) N> is possible, which allows instantiation of N as a reference:
还要注意,甚至template<decltype(auto> N>也是可能的,它允许将N初始化为引用。
template<decltype(auto) N> class C { … }; int i; C<(i)> x; // N为 int&(注意,是个引用类型)
See Section 15.10.1 on page 296 for details.
更多详细信息,请参阅第296页的15.10.1节。
3.5 Summary
3.5 小结
• Templates can have template parameters that are values rather than types.
模板可以具有值模板参数,而不是类型模板参数。
• You cannot use floating-point numbers or class-type objects as arguments for nontype template parameters. For pointers/references to string literals, temporaries, and subobjects, restrictions apply.
对于非类型模板参数,你不能使用浮点数、class类型的对象。对于字符串字面量、临时对象及其子对象的的指针/引用类型,有一些限制。
• Using auto enables templates to have nontype template parameters that are values of generic types.
使用auto可以使非类型模板参数具有泛型类型的值。