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

C++ 编程技巧之StrongType(1)

最近看到一个NamedType的开源库,被里面的Strong Type这个概念和里面的模版实现给秀了一脸,特此总结学习一下

GitHub - joboccara/NamedType: Implementation of strong types in C++

C++本身是一种强类型语言,类型包括int、double等这些build in类型 以及class类型,强类型的意思是所有变量的类型在编译阶段应该都是确定的。上面提到的Strong Type并不是这个意思,在C++中,Strong Type(强类型)是一种编程技巧,是对C++的类型进行加强的意思。

它用于定义一种类型安全的机制,使得编译器能够区分语义相似但实际意义不同的类型,从而避免因类型混淆导致的潜在错误。这种技术常见于处理需要区分类似数据的场景,比如标识符、单位、或封装基本数据类型以增强可读性和安全性。Strong Type在某种程度上和C++的自定义字面量有点相似,本文也对他们进行一些对比。

强类型的基本思想

强类型通常是通过封装一个原始类型(如 int, double 等)实现的,我们可以先看如下示例来感受一下用法

通过简单封装赋予语义

struct Meters {
double value;
};struct Seconds {
double value;
};Meters operator+(Meters lhs, Meters rhs) {return Meters{lhs.value + rhs.value};
}// 用法
Meters distance1{5.0};
Meters distance2{10.0};
Meters total = distance1 + distance2; // OK
// Seconds time = distance1; // 编译错误,类型不兼容
通过模板实现通用强类型
template <typename Tag, typename T>
class StrongType {
public:
explicit StrongType(T value) : value_(value) {}
T get() const { return value_; }private:
T value_;
};// 标识语义的标签
struct UserIdTag {};
struct ProductIdTag {};using UserId = StrongType<UserIdTag, int>;
using ProductId = StrongType<ProductIdTag, int>;UserId uid(42);
ProductId pid(101);// pid = uid; // 编译错误,类型不兼容
强类型与操作符重载

如果需要增强强类型的可用性,可以为其定义操作符:

template <typename Tag, typename T>
class StrongType {
public:
explicit StrongType(T value) : value_(value) {}
T get() const { return value_; }// 支持操作符重载
StrongType operator+(const StrongType& other) const {return StrongType(value_ + other.value_);
}private:
T value_;
};struct DistanceTag {};
using Distance = StrongType<DistanceTag, double>;// 用法
Distance d1(5.0), d2(10.0);
Distance total = d1 + d2; // OK

应用场景

  • 标识符区分: 避免混淆例如用户 ID 和产品 ID 等数据。
  • 物理量单位: 避免直接使用 double 等基础类型混淆不同单位的数值(如米和秒)。
  • 类型安全接口: 增强类型安全,避免误用相似类型的数据。
  • 业务逻辑抽象: 为关键业务对象创建显式的类型,增强代码可读性和可维护性。

Strong Type 和 C++字面量

上面Strong Type的思想其实和C++字面量有异曲同工之妙,我们不妨也来总结一下C+++自定义字面量的知识。

用户自定义字面量是 C++ 提供的扩展功能,通过为特定的字面量(如整型、浮点型、字符串等)自定义行为,可以实现特定语义,例如单位转换、数据封装等。自定义字面量通常使用 operator"" 实现。

基本语法

用户自定义字面量是通过重载 operator"" 实现的,后面可以接字面量标识符。根据传入的参数类型,用户自定义字面量分为以下几种形式:

参数类型

示例

常用场景

const char*

"example"_id

处理字符串类型

unsigned long long

42_kg

处理整型字面量

long double

3.14_m

处理浮点型字面量

char

'c'_symbol

处理字符字面量

const char*, size_t

"hello"_custom

处理字符串并获得长度

自定义字面量实现

以下是针对不同类型的自定义字面量的实现方式:

整型字面量
#include <iostream>// 自定义字面量,用于单位转换
constexpr unsigned long long operator"" _kg(unsigned long long value) {return value * 1000; // 转换为克
}int main() {auto weight = 5_kg; // 表示 5 公斤std::cout << "Weight in grams: " << weight << " g\n"; // 输出 5000 greturn 0;
}
浮点型字面量
#include <iostream>// 自定义字面量,用于单位转换
constexpr long double operator"" _m(long double value) {return value * 100; // 转换为厘米
}int main() {auto length = 2.5_m; // 表示 2.5 米std::cout << "Length in cm: " << length << " cm\n"; // 输出 250 cmreturn 0;
}

字符串字面量

#include <iostream>
#include <string>// 自定义字面量,处理字符串
std::string operator"" _hello(const char* str, size_t) {return std::string("Hello, ") + str;
}int main() {auto greeting = "World"_hello; // 添加 "Hello, "std::cout << greeting << std::endl; // 输出 "Hello, World"return 0;
}
字符串和长度字面量

C++20 前,可以通过传递字符串和长度参数实现更灵活的功能:

#include <iostream>// 自定义字面量,打印字符串和长度
void operator"" _log(const char* str, size_t len) {std::cout << "String: " << str << ", Length: " << len << std::endl;
}int main() {"Hello World"_log; // 输出 String: Hello World, Length: 11return 0;
}

C++自定义字面量也可以给变量赋予直接的语义信息,但是功能比较单一,而Strong Type通过类的封装,可以实现更复杂的功能。

Strong Type 和 C++字面量结合

#include <iostream>// 定义强类型 Distance
struct Distance {explicit Distance(double meters) : meters_(meters) {}double getMeters() const { return meters_; }
private:double meters_;
};// 自定义字面量
constexpr Distance operator"" _km(long double value) {return Distance(static_cast<double>(value * 1000.0));
}int main() {Distance d = 1.5_km; // 使用字面量创建强类型对象std::cout << "Distance: " << d.getMeters() << " meters" << std::endl; // 输出 1500 metersreturn 0;
}

总结

据说美国航空航天局火星气候探测者号的失联事故是因为代码中两种测量系统不匹配导致的(https://en.wikipedia.org/wiki/Mars_Climate_Orbiter), 如果能采取上面的编程技术,或许能避免这种事故的产生,当然这是后话。

下面总结一下上面编程技巧:

优点

  • 提高类型安全性。
  • 增强代码可读性。
  • 防止逻辑错误。

缺点

  • 增加一定的代码复杂性。
  • 性能开销(通常可以忽略)。

我将在下一篇中对上面的NamedType源码进行一些解读。

相关文章:

C++ 编程技巧之StrongType(1)

最近看到一个NamedType的开源库&#xff0c;被里面的Strong Type这个概念和里面的模版实现给秀了一脸&#xff0c;特此总结学习一下 GitHub - joboccara/NamedType: Implementation of strong types in C C本身是一种强类型语言&#xff0c;类型包括int、double等这些build i…...

芯片测试-smith圆图

smith圆图 &#x1f4a2;smith圆图的故事&#x1f4a2;&#x1f4a2;smith圆图中的各部分来历&#x1f4a2;&#x1f4a2;公式推导&#x1f4a2;&#x1f4a2;等电阻圆特点&#x1f4a2;&#x1f4a2;等电抗圆&#x1f4a2;&#x1f4a2;等电抗圆特点&#x1f4a2; &#x1f4a…...

HTML技术深度解析:构建现代网页的基石

引言 HTML&#xff08;HyperText Markup Language&#xff0c;超文本标记语言&#xff09;是构建网页和网上应用的标准标记语言。随着互联网技术的飞速发展&#xff0c;HTML已经成为前端开发中不可或缺的核心技术之一。本文将深入探讨HTML的基本概念、核心元素、最新发展以及在…...

Leecode刷题C语言之判断是否可以赢得数字游戏

执行结果:通过 执行用时和内存消耗如下&#xff1a; bool canAliceWin(int* nums, int numsSize) {int single_digit_sum 0;int double_digit_sum 0;for (int i 0; i < numsSize; i) {if (nums[i] < 10) {single_digit_sum nums[i];} else {double_digit_sum nums[…...

Ubuntu 关机命令

在 Ubuntu 系统中&#xff0c;有几种方法可以关机。以下是常用的关机命令及其说明&#xff1a; 1. 使用 shutdown 命令 shutdown 命令是最常用和最灵活的关机方式。它可以设置定时关机&#xff0c;并且可以发送警告消息给所有登录用户。 立即关机 sudo shutdown now定时关机…...

数据采集中,除了IP池的IP被封,还有哪些常见问题?

在数据采集的过程中&#xff0c;代理IP池的使用无疑为我们打开了一扇通往信息宝库的大门。然而&#xff0c;除了IP被封禁这一常见问题外&#xff0c;还有许多其他问题可能影响数据采集的效果。本文将探讨在数据采集中&#xff0c;除了IP被封之外&#xff0c;还可能遇到的一些常…...

【Anaconda】 创建环境报错:CondaHTTPError: HTTP 000 CONNECTION FAILED for url

问题描述 使用 Anaconda 创建环境时报错&#xff1a; CondaHTTPError: HTTP 000 CONNECTION FAILED for url <https://repo.anaconda.com/pkgs/free/noarch/repodata.json.bz2> Elapsed: -An HTTP error occurred when trying to retrieve this URL. HTTP errors are o…...

社交电商破局之“2+1 链动模式 O2O 商城小程序源码”赋能流量困境突围

摘要&#xff1a;本文聚焦于当下商家在流量困境中挣扎的现状&#xff0c;剖析传统电商高流量成本、平台流量获取难等痛点&#xff0c;阐述私域流量池兴起的缘由与价值。重点探究“21 链动模式 O2O 商城小程序源码”如何融入社交电商架构&#xff0c;通过创新机制与线上线下融合…...

【ArcGIS Pro微课1000例】0062:ArcGIS Pro3.3.1中文版安装教程(附安装包下载)

本文讲述ArcGIS Pro3.3.1中文版安装教程(附安装包下载)。 文章目录 一、ArcGIS Pro3.3.1中文版下载二、ArcGIS Pro3.3.1中文版安装一、ArcGIS Pro3.3.1中文版下载 【订阅专栏】,获取完整安装包及专栏配套实验数据。下载后解压,如下图所示: 二、ArcGIS Pro3.3.1中文版安装…...

Linux - web服务器

四、web服务器 1、基础知识 URL&#xff1a;Uniform Resource Locator&#xff0c;统一资源定位符&#xff0c;对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示&#xff0c;是互联网上标准资源的地址。 网址格式&#xff1a;<协议>://<主机或主机名&g…...

设计模式-适配器模式-注册器模式

设计模式-适配器模式-注册器模式 适配器模式 如果开发一个搜索中台&#xff0c;需要适配或接入不同的数据源&#xff0c;可能提供的方法参数和平台调用的方法参数不一致&#xff0c;可以使用适配器模式 适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至…...

减速机润滑油更换的最佳周期是多久?

减速机是工业设备中的重要组成部分&#xff0c;润滑油的使用对于其正常运转和寿命具有至关重要的作用。那么&#xff0c;减速机多久更换一次润滑油呢&#xff1f;实际上&#xff0c;减速机润滑油的更换周期受多种因素影响&#xff0c;以下是一些具体的更换周期建议&#xff1a;…...

程序执行堆栈执行模拟

所有的文件都是在硬盘&#xff08;磁盘&#xff09;上&#xff0c;调用时先调用javac指令的jdk编译成.class然后被java指令的jre送到内存中&#xff0c;java在内存中有自己的一片区域叫JVM&#xff0c;编译进来的文件首先进入方法区。 staitc的属性就是在进入内存的时候开辟了一…...

《Python基础》之数据加密模块hashlib的用法

目录 一、简介 二、用法 步骤一、导入hashlib库 步骤二、创建哈希对象 步骤三、往哈希对象中传值 1、可以在创建对象的时候传值 2、使用updata传值 步骤四、获取经过哈希对象加密后的值 三、注意事项 1、编码问题 2、安全性 3、多次传值 四、总结 一、简介 hashli…...

安装Fcitx5输入框架和输入法自动部署脚本(来自Mark24)-Ubuntu通用

在Ubuntu22.04上安装rime中文输入法的基本教程 上述文章接近废弃。 使用新逻辑配置基本的Fcitx5的输入法。 安装 第一步&#xff0c;下载相关组件 sudo nala install vim sudo nala install ruby sudo nala install fcitx5-rime第二步&#xff0c;设置语言为Fcitx5 而非 默认…...

【IMF靶场渗透】

文章目录 一、基础信息 二、信息收集 三、flag1 四、flag2 五、flag3 六、flag4 七、flag5 八、flag6 一、基础信息 Kali IP&#xff1a;192.168.20.146 靶机IP&#xff1a;192.168.20.147 二、信息收集 Nmap -sP 192.168.20.0/24 Arp-scan -l nmap -sS -sV -p- -…...

Zookeeper选举算法与提案处理概览

共识算法(Consensus Algorithm) 共识算法即在分布式系统中节点达成共识的算法&#xff0c;提高系统在分布式环境下的容错性。 依据系统对故障组件的容错能力可分为&#xff1a; 崩溃容错协议(Crash Fault Tolerant, CFT) : 无恶意行为&#xff0c;如进程崩溃&#xff0c;只要…...

深入了解 Adam 优化器对显存的需求:以 LLaMA-2 7B 模型为例 (中英双语)

中文版 深入了解 Adam 优化器对显存的额外需求&#xff1a;模型参数与优化器状态的显存开销分析 在深度学习模型的训练过程中&#xff0c;显存是一个关键的资源&#xff0c;尤其在处理大型语言模型或深度神经网络时。训练时的显存需求不仅包括模型参数本身&#xff0c;还涉及…...

数据分析学习

数据分析的定义 数据分析是通过对收集到的数据进行清理、转换、建模、分析和解释&#xff0c;从中提取有用的信息和洞察&#xff0c;以帮助做出更好的决策。数据分析可以应用于各种领域&#xff0c;比如商业、金融、医疗、市场营销等&#xff0c;目的是通过数据来发现模式、趋…...

PaddleOCR:一款高性能的OCR工具介绍

一、引言 随着人工智能技术的不断发展&#xff0c;光学字符识别&#xff08;OCR&#xff09;技术在各行各业得到了广泛应用。OCR技术能够将图片、扫描件等非结构化数据中的文字信息提取出来&#xff0c;转换为可编辑的文本格式。在我国&#xff0c;百度开源了一款优秀的OCR工具…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...