template之非类型模板参数

本文深入探讨C++中非类型模板参数的使用,包括其语法特点、编译期实例化特性及如何利用它自动获取数组大小,避免手动传递大小参数的繁琐。同时,通过具体示例展示了非类型模板参数在函数实现中的应用,如数组大小获取和字符串比较。

前言

前一节分析了关于template的使用注意, 本节分析关于template非类型参数的使用, 非类型参数可能在有些人认为并没有太大作用, 但是既然C++规定有能这样使用就肯定有其意义, 这里就做一个浅析.

非类型模板参数

非类型参数, 可用在模板中自定义为整型类型, 指针或引用, 不能定义为浮点数等其他类型.

非类型模板参数在编译期间就已经实例化, 所以其模板实参必须是常量表达式.

template<int N>; 	// N是编译时就确定的常量表达式
template<size_t N, size_t M>;	// N,M是编译时就确定的常量表达式

可能就是会觉得没有用, 毕竟使用模板就是要用他的模板参数类型啊, 没有这个那还怎么用. 这里就来先看一个例子.

要求: 实现一个函数返回一个数组的真实大小, 如 : int a[100]; ArrSize(a);返回100

嗯? 讲道理传入函数中a就转换为指针了, 怎么用指针能获取其表示范围? 这里就要用到template的非类型参数.

template<class T, std::size_t N>	// 这里的N是编译时期就知道了, 所以可以加上constexpr关键字
    constexpr std::size_t ArrSize(T (&a)[N]) noexcept	
{
    return N;
}
int a[100]; ArrSize(a);

实现了这个功能后我们就来分析一下.

函数模板通过传入a后会自动推导出 T 的类型为 int, N 的大小为 100, 函数通过引用, 所以传入的是一个a而不是一个指针.

重点在于模板参数N能自动推导传入数组的大小.

同样我们可以将strcmp做一个封装, 实现一个字符串比较的模板函数.

template<unsigned N, unsigned M>
bool compare(const char (&a)[N], const char (&b)[M])
{
    return strcmp(a, b);
}

总结

使用template的非类型参数可以自动帮我们获取参数的大小, 省去手动传入参数大小的麻烦等问题. 记住 : 非类型模板参数在编译期间就已经实例化, 所以其模板实参必须是常量表达式.

<think>好的,我现在需要解释类型模板参数类型模板参数的区别和各自的用途。用户之前已经问过关于`template<size_t N>`的问题,现在他们想更全面地了解两种模板参数的不同。首先,我应该回顾一下之前的回答,确保不重复但补充更多细节。 首先,我需要明确两者的基本定义。类型模板参数是传递类型,比如`typename T`,而类型模板参数传递的是具体的值,比如整数、指针或引用。然后,我需要比较它们的应用场景,比如类型参数用于泛型编程,而类型参数常用于指定大小或常量值。 接着,我应该考虑用户可能的疑问点。比如,为什么需要类型参数?什么时候应该用类型参数?可能用户对两者的选择存在困惑。需要举一些实际的例子,比如std::array使用类型参数来指定数组大小,而std::vector使用类型参数来指定元素类型。 还要注意语法上的区别。类型参数使用`typename`或`class`关键字,而类型参数需要指定具体的类型,比如`int`、`size_t`等。此外,类型参数的值必须在编译时确定,这涉及到constexpr或常量表达式的问题。 可能还需要讨论两者的优缺点。比如类型参数会导致代码膨胀,因为每个不同的值都会生成新的实例,而类型参数则更灵活,但可能需要更多的模板特化。 另外,用户可能对C++20的新特性感兴趣,比如类型模板参数支持浮点数或类类型(需要满足某些条件),这可以稍微提一下,但可能不需要深入,除用户明确问到。 最后,总结两者的核心差异,帮助用户根据需求选择适合的模板参数类型。确保语言通俗易懂,结构清晰,使用示例来辅助说明,比如用数组容器和算法优化的例子。</think>以下是关于C++中**类型模板参数**与**类型模板参数**的对比解析,用表格形式直观呈现核心差异: --- ### **1. 基本定义对比** | 维度 | 类型模板参数 | 类型模板参数 | |--------------------|----------------------------------|------------------------------------| | **传递内容** | 数据类型(如`int`, `std::string`) | 具体的数值(如`5`, 指针,编译期常量)| | **语法形式** | `template<typename T>` | `template<int N>` / `template<auto N>`(C++17+) | | **典型应用** | 泛型容器(如`std::vector<T>`) | 固定尺寸结构(如`std::array<T, N>`)| --- ### **2. 核心差异详解** #### **① 参数本质** | 类型参数 | 类型参数 | |---------|------------| | ```cpp template<typename T> class Box { T content; // T可以是任意类型 }; ``` | ```cpp template<int Size> class Buffer { char data[Size]; // Size必须是编译期常量 }; ``` | #### **② 代码生成机制** | 类型参数 | 类型参数 | |---------|------------| | 每个不同的**类型**生成独立实例<br>(`Box<int>`和`Box<float>`是不同类) | 每个不同的**数值**生成独立实例<br>(`Buffer<10>`和`Buffer<20>`是不同类) | #### **③ 使用限制** | 类型参数 | 类型参数 | |---------|------------| | 可接受任何合法类型(包括用户自定义类型) | 仅允许特定类型:整数、枚举、指针、引用(C++20起支持浮点型和字面量类类型) | | ```cpp template<typename T> void print(T val); // 接受任意类型参数 ``` | ```cpp template<size_t N> struct Array { // N必须是编译期常量表达式 }; ``` | --- ### **3. 典型应用场景** #### **类型模板参数** ```cpp // 通用容器 template<typename T> class Stack { std::vector<T> elements; public: void push(const T& value); T pop(); }; // 类型安全接口 template<typename Input, typename Output> Output convert(const Input& value); ``` #### **类型模板参数** ```cpp // 固定大小数组(尺寸编译期确定) template<typename T, size_t N> class StaticArray { T data[N]; }; // 编译期计算(模板元编程) template<int N> struct Factorial { static const int value = N * Factorial<N-1>::value; }; template<> struct Factorial<0> { value = 1; }; // 循环展开优化 template<int UnrollCount> void process() { // 编译器生成UnrollCount次展开的代码 } ``` --- ### **4. 代码示例对比** #### **类型参数示例** ```cpp template<typename T> T add(T a, T b) { return a + b; } // 使用: add<int>(3, 5); // 返回8 add<double>(2.5, 3.7); // 返回6.2 ``` #### **类型参数示例** ```cpp template<size_t N> void printNTimes(const std::string& msg) { for (size_t i = 0; i < N; ++i) std::cout << msg << "\n"; } // 使用: printNTimes<3>("Hello"); // 打印3次Hello ``` --- ### **5. 混合使用示例** ```cpp template<typename T, size_t Capacity> class FixedVector { T data[Capacity]; size_t size = 0; public: void push_back(const T& value) { if (size < Capacity) data[size++] = value; } }; // 使用: FixedVector<double, 100> measurements; // 存储最多100个double ``` --- ### **6. 选择指南** | **使用类型模板参数当** | **使用类型模板参数当** | |--------------------------------------|----------------------------------------| | 需要操作**不同类型的数据** | 需要固定**编译期已知的数值** | | 实现通用算法(如排序、查找) | 定义资源约束(如数组长度、矩阵维度) | | 构建类型无关的接口 | 进行模板元编程或编译期计算 | | 需要支持用户自定义类型 | 优化性能(如循环展开、内存布局控制) | --- ### **7. 高级特性** #### **类型参数** - 支持模板模板参数: ```cpp template<template<typename> typename Container> class Adapter { Container<int> storage; // 使用传入的容器类型 }; ``` #### **类型参数** - C++20支持类类型作为类型参数(需满足`constexpr`构造等条件): ```cpp struct Point { int x, y; }; template<Point P> class Widget { /* 使用P.x和P.y */ }; ``` --- ### **总结** - **类型模板参数**:实现**数据类型抽象**,构建通用组件 - **类型模板参数**:实现**数值参数化**,优化编译期行为 - 实际开发中常**组合使用**两者,例如`std::array<T, N>`同时指定元素类型和数组长度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值