当前位置: 首页 > article >正文

C++那些事之变量模版

C++那些事之变量模版

开篇先给大家引入一个星友发的代码,这段代码你猜结果是什么?

a) 编译错误

b) 运行ok,请说明结果。

f518c07bd742b0d6e245f8ebe6e97141.png

下面就来揭秘这道问题吧

在编写模板代码时,我们经常需要定义和使用各种类型特征(type traits)。这些类型特征通常用于在编译期对类型进行判断,STL 提供的 std::is_conststd::is_samestd::is_reference 就是这样的例子。通过这些特征,开发者可以根据类型属性进行编译期决策。

本文将介绍如何利用 C++14 变量模板(variable templates) 来简化类型特征的编写,并提供一个简单的示例。

注:本文的所有代码已更新于星球。

C++11

在 C++11 中,编写类型特征通常要使用 structstd::integral_constant 来封装布尔值。例如,我们要编写一个判断类型是否为 float 的类型特征 is_float,可以通过如下方式实现:

template <typename T>
struct is_float {
    static constexpr bool value = std::is_same<T, float>::value;
};

或者稍微简洁一点,使用类型别名和 std::integral_constant

template <typename T>
using is_float = std::integral_constant<bool, std::is_same<T, float>::value>;

尽管这比直接使用 struct 更简洁,最终我们仍然是在定义一个类型,而不是直接得到布尔值。要使用这个特征时,需要通过 ::value 成员来获取布尔值:

template <typename T>
void test(T t){
    if (is_float<T>::value){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

这样虽然可以工作,但仍存在两方面的不足:

  1. 每次使用类型特征时都要访问 ::value

  2. 定义新的类型特征时必须声明新的类型或类型别名,增加了代码冗余。

C++14 变量模板的引入

C++14 引入了 变量模板,允许我们直接定义依赖于类型的变量。这一特性简化了类型特征的编写,使得我们不再需要使用类型别名或 struct 来封装布尔值,而是可以直接定义布尔值。

重写后的 is_float 特征如下:

template <typename T>
constexpr bool is_float = std::is_same<T, float>::value;

这种方式显得更加简洁明了,去除了不必要的代码。使用起来也更方便:

template <typename T>
void test(T t){
    if (is_float<T>){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

相比之前的写法,使用变量模板后,不再需要通过 ::value 来访问布尔值,代码更加简洁直观。

变量模板的优势

  1. 减少冗余代码:变量模板允许我们直接定义基于类型的变量,避免了使用 structstd::integral_constant 封装布尔值的冗余代码。

  2. 提高可读性:变量模板通过简化语法,使代码更清晰,尤其是在编写大量类型特征时。

  3. 支持模板特化:与类型别名模板不同,变量模板可以进行特化,无论是全特化还是部分特化。我们可以为特定类型定义专门的逻辑,而不必担心扩展性问题。

C++17 的进一步改进

C++17 进一步扩展了变量模板的应用,为每个类型特征提供了变量模板的辅助工具。例如,std::is_same 引入了 std::is_same_v 变量模板,用于简化访问类型特征值:

template <typename T>
constexpr bool is_float = std::is_same_v<T, float>;

通过这种方式,类型特征的使用更加简洁,开发者可以直接利用变量模板而无需每次访问 ::value

实际案例

这项特性在实际领域使用的挺多的,例如:数据库当中分配不同大小的内存块,我们可以通过模版初始化不同类型的块大小。

template <typename T>
constexpr size_t block_size = 1024;  // 默认大小

template <>
constexpr size_t block_size<double> = 2048;  // double 类型需要更多内存

一起探索更多C++项目/知识~

a4b09751ad613ade319a538173ad2b30.png

往期推荐:

向量数据库milvus源码剖析之开篇

热度更新,手把手实现工业级线程池

玩转cpp小项目星球3周年了!

93cf14ed8649cae83bc8fe839a73b98a.jpeg


http://www.kler.cn/a/324340.html

相关文章:

  • Ubuntu24 上安装搜狗输入法
  • 树莓派4B Qt+FFMPEG 多线程录制USB相机mjpeg数据流“h264_omx“硬件编码的MP4文件
  • 深入List集合:ArrayList与LinkedList的底层逻辑与区别
  • IDEA2024:右下角显示内存
  • 工化企业内部能源能耗过大 落实能源管理
  • 深入理解Flutter生命周期函数之StatefulWidget(一)
  • 大厂面试真题-说一下Mybatis的缓存
  • 【分布式微服务云原生】详细介绍下dubbo和springcloud所能支持的微服务特性,为啥能支持的技术原理,以及适用的业务场景,并对两者各方面做个详细的比较
  • Qt/C++ 解决调用国密SM3,SM4加密解密字符串HEX,BASE64格式转换和PKCS5Padding字符串填充相关问题
  • Java线程基础
  • SQL CREATE TABLE 语句
  • TypeScript概念讲解
  • DePIN 代表项目 CESS 受邀出席国会山活动,向议员展示创新 DePIN 技术
  • 阿里rtc云端录制TypeScript版NODE运行
  • HarmonyOS安全能力介绍
  • 240927-各种卷积最清晰易懂blender动画展示
  • Spark 的 Skew Join 详解
  • Spring Boot 2.4.3 + Java 8 升级为 Java 21 + Spring Boot 3.2.0
  • ubuntu 不用每次输入sudo的四种方式
  • 基于python+django+vue的电影数据分析及可视化系统
  • 滚雪球学MySQL[6.1讲]:数据备份与恢复
  • 初始MYSQL数据库(6)—— 事务
  • 什么东西可以当做GC Root,跨代引用如何处理?
  • 【LLM】从零预训练一个tiny-llama
  • python高级用法_装饰器
  • text2sql方法:NatSQL和DIN-SQL