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&

参考 http://www.tmplbook.com

到此这篇关于C++详解非类型模板参数Nontype与Template及Parameters的使用的文章就介绍到这了,更多相关C++非类型模板参数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++模板全方位深入解读

    目录 1.泛型编程 2.函数模板 概念 函数模板的格式 函数模板的原理 函数模板的实例化 隐式实例化 显式实例化 模板参数的匹配原则 3.类模板 (1) 类模板的定义格式 (2) 类模板的实例化 4.非类型模板参数 5.模板特化 (1)函数模板的特化 (2)类模板的特化 全特化 偏特化 6.模板的分离编译 问题分析 1.泛型编程 如何实现一个通用的交换函数? 这点函数重载可以做到,比如一下Swap函数的重载,分别重载了俩种不同参数类型的Swap void Swap(int& x, int&

  • c++分离讲解模板的概念与使用

    目录 泛类编程 函数模板 函数模板的概念 函数模板的使用 函数模板的实例化 函数模板的匹配原则 类模板 类模板的定义格式 类模板的实例化 泛类编程 学习模板,首先我们需要了解一下什么是泛类编程 #include<iostream> using namespace std; int add(int a, int b) { return a + b; } double add(double a, double b) //这两个add构成了函数重载 { return a + b; } int mai

  • C++11 模板参数的“右值引用”是转发引用吗

    在C++11中,&&不再只有逻辑与的含义,还可能是右值引用: void f(int&& i); 但也不尽然,&&还可能是转发引用: template<typename T> void g(T&& obj); "转发引用"(forwarding reference)旧称"通用引用"(universal reference),它的"通用"之处在于你可以拿一个左值绑定给转发引用

  • C++模板Template详解及其作用介绍

    目录 1. 模板 2. 函数模板 2.1 函数模板概念 2.2 函数模板格式 2.3 函数模板原理 2.4 函数模板的实例化 2.5 模板参数的匹配原则 2.6声明定义分离 3. 类模板 3.1 类模板格式 3.2 类模板的实例化 3.3 类模板中函数放在类外进行定义时 4. 模板分离编译 4.1 什么是分离编译 4.2 模板的分离编译 5. 缺省值与返回值 6. 总结 1. 模板 首先模板分为函数模板和类模板 想到模板,就会联想到泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手

  • C++ 深入浅出探索模板

    目录 非类型模板参数 模板特化 函数模板特化 类模板特化 全特化 偏特化 模板分离编译 模板的分离编译 解决方法 总结 非类型模板参数 模板参数分类类型形参与非类型形参. 类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称. 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用. 注意: 浮点数,类对象以及字符串是不允许作为非类型模板的. 非类型的模板参数必须在编译期就能确认结果. 模板特化 有时候,编译默认函数模板

  • C++超详细讲解模板的使用

    目录 一.函数模板 1.1函数模板概念 1.2 函数模板格式 1.3 函数模板的原理 1.4 函数模板的实例化 二.类模板 2.1 类模板的定义格式 2.2类模板的实例化 总结 一.函数模板 1.1函数模板概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本. 1.2 函数模板格式 template<typename T1, typename T2,…,typename Tn> 返回值类型 函数名(参数列表){} template<

  • C++可变参数模板的展开方式

    文章目录 前言可变参数模板的定义参数包的展开递归函数方式展开逗号表达式展开enable_if方式展开折叠表达式展开(c++17) 总结 前言 可变参数模板(variadic templates)是C++11新增的强大的特性之一,它对模板参数进行了高度泛化,能表示0到任意个数.任意类型的参数.相比C++98/03这些类模版和函数模版中只能含固定数量模版参数的“老古董”,可变模版参数无疑是一个巨大的进步. 如果是刚接触可变参数模板可能会觉得比较抽象,使用起来会不太顺手,使用可变参数模板时通常离不开模

  • C++深入了解模板的使用

    目录 一.泛型编程 二.函数模板 1.函数模板概念 2.函数模板格式 3.函数模板的原理 三.类模板 一.泛型编程 泛型编程:不再是针对某种类型,能适应广泛的类型,跟具体的类型无关的代码 如何实现一个通用的交换函数呢? void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { double

  • C++详解非类型模板参数Nontype与Template及Parameters的使用

    目录 非类型类模板参数 非类型函数模板参数 非类型模板参数的限制 非类型模板参数 auto 非类型类模板参数 前一章使用的例子 Stack 使用的是标准库中的容器管理元素,也可以使用固定大小的 std::array,它的优势是内存管理开销更小,数组的大小可以交给用户指定. #include <array> #include <cassert> template<typename T, std::size_t Maxsize> class Stack { private:

  • 详解SpringBoot中的参数校验(项目实战)

    Java后端发工作中经常会对前端传递过来的参数做一些校验,在业务中还要抛出异常或者不断的返回异常时的校验信息,充满了if-else这种校验代码,在代码中相当冗长.例如说,用户注册时,会校验手机格式的正确性,用户名的长度等等.虽说前端也可以做参数校验,但是为了保证我们API接口的可靠性,以保证最终数据入库的正确性,后端进行参数校验不可忽视. Hibernate Validator 提供了一种统一方便的方式,让我们快速的实现参数校验. Hibernate Validator 使用注解,实现声明式校验

  • TypeScript语法详解之类型操作的补充

    目录 类型操作的补充 类型断言的使用 非空类型的断言 可选链使用介绍 两个特殊操作符 字面量类型介绍 字面量推理介绍 总结 类型操作的补充 类型断言的使用 有时候TypeScript无法获取具体的类型信息,这个我们需要使用类型断言( Type Assertions). 比如我们在操作DOM的时候, 通过 document.getElementById 获取元素, TypeScript只知道该函数会返回HTMLElement ,但并不知道它具体的类型, 是无法得知具体是img还是div等等: 当我

  • 详解python3类型注释annotations实用案例

    1.类型注解简介 Python是一种动态类型化的语言,不会强制使用类型提示,但为了更明确形参类型,自python3.5开始,PEP484为python引入了类型注解(type hints) 示例如下: 2.常见的数据类型 int,long,float: 整型,长整形,浮点型 bool,str: 布尔型,字符串类型 List, Tuple, Dict, Set: 列表,元组,字典, 集合 Iterable,Iterator: 可迭代类型,迭代器类型 Generator:生成器类型 Sequence

  • 详解非极大值抑制算法之Python实现

    一.概述 这里不讨论通用的NMS算法(参考论文<Efficient Non-Maximum Suppression>对1维和2维数据的NMS实现),而是用于目标检测中提取分数最高的窗口的.例如在行人检测中,滑动窗口经提取特征,经分类器分类识别后,每个窗口都会得到一个分数.但是滑动窗口会导致很多窗口与其他窗口存在包含或者大部分交叉的情况.这时就需要用到NMS来选取那些邻域里分数最高(是行人的概率最大),并且抑制那些分数低的窗口. NMS在计算机视觉领域有着非常重要的应用,如视频目标跟踪.数据挖掘

  • C++详解如何通过模板实现元素的反序

    目录 所涉知识点 模板概念 示例代码 开发环境 运行结果 注意 所涉知识点 阅读此文需要掌握的知识点:回调函数,模板类,类模板,栈. 模板概念 首先模板分为函数模板和类模板 想到模板,就会联想到泛型编程 泛型编程:编写与类型无关的通用代码,是代码复用的一种手段.模板是泛型编程的基础. 网图: 在之前,我们已经知道了函数重载 还是那一个例子 Swap函数交换 int double char 哪怕是函数重载,我们也要写三个,但是如果有了模板,我们只需要: 告诉编译器一个模板,让编译器根据不同的类型利

  • 详解Oracle在out参数中访问光标

    详解Oracle在out参数中访问光标 一 概念 申明包结构 包头:负责申明 包体:负责实现 二 需求 查询某个部门中所有员工的所有信息 三 包头 CREATE OR REPLACE PACKAGE MYPACKAGE AS type empcursor isref cursor; procedure queryEmplist(dno in number,emplist out empcursor); END MYPACKAGE; 四 包体 包体需要实现包头中声明的所有方法 CREATE OR

  • 详解springboot设置默认参数Springboot.setDefaultProperties(map)不生效解决

    我们都知道springboot 由于内置tomcat(中间件)直接用启动类就可以启动了. 而且我们有时想代码给程序设置一些默认参数,所以使用方法Springboot.setDefaultProperties(map) SpringApplication application = new SpringApplication(startClass); // Map<String, Object> params = new HashMap<>(); params.put("l

  • 详解C++函数模板与分离编译模式

    1.分离编译模式 一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件连接起来形成单一的可执行文件的过程称为分离编译模式. 2.使用函数模板在链接时出错 在C++程序设计中,在一个源文件中定义某个函数,然后在另一个源文件中使用该函数,这是一种非常普遍的做法.但是,如果定义和调用一个函数模板时也采用这种方式,会发生编译错误. 下面的程序由三个文件组成:func.h用来对函数模板进行申明,func.cpp用来定义函数模板,main.cpp包含func.h头文件

  • SpringBoot开发详解之Controller接收参数及参数校验

    目录 Controller 中注解使用 传输参数的几种Method 获取参数的几种常用注解 使用对象直接获取参数 使用@Valid对参数进行校验 总结 Controller 中注解使用 接受参数的几种传输方式以及几种注解: 在上一篇中,我们使用了JDBC链接数据库,完成了简单的后端开发.但正如我在上文中抛出的问题,我们能不能更好的优化我们在Controller中接受参数的方式呢?这一篇中我们就来聊一聊怎么更有效的接收Json参数. 传输参数的几种Method 在定义一个Rest接口时,我们通常会

随机推荐