C++库std::clamp

news2024/11/13 11:04:57

C++库std::clamp

std::clamp: 轻松掌握值的范围限制

icon

目录

    • 1. 引言
    • 2. std::clamp 基本概念
      • 2.1 函数签名
      • 2.2 参数说明
      • 2.3 返回值
    • 3. 基本用法
    • 4. 深入理解 std::clamp
      • 4.1 实现原理
      • 4.2 注意事项
    • 5. 高级用法
      • 5.1 自定义比较函数
      • 5.2 与 lambda 表达式结合
    • 6. 实际应用场景
      • 6.1 图形编程
      • 6.2 游戏开发
      • 6.3 信号处理,参数读写
    • 7. std::clamp vs 其他方法
      • 7.1 vs 手动实现
      • 7.2 vs std::min 和 std::max 组合
      • 7.3 vs Qt库的qBound函数
    • 8. 性能考虑
    • 9. 常见问题与解决方法
      • 9.1 编译错误: 'clamp' is not a member of 'std'
      • 9.2 自定义类型的 clamp 操作失败
      • 9.3 浮点数精度问题


1. 引言

在 C++ 编程中,我们经常需要将一个值限制在特定的范围内。这种操作在图形编程、游戏开发、信号处理等领域非常常见。C++17 引入了 std::clamp 函数,它提供了一种简洁、高效的方式来实现这一功能。本文将深入探讨 std::clamp 的使用方法、实现原理、应用场景以及与其他方法的对比。



2. std::clamp 基本概念

std::clamp 是 C++17 在 <algorithm> 头文件中引入的一个函数模板。它的作用是将一个值限制在指定的范围内。

2.1 函数签名

template< class T >
constexpr const T& clamp( const T& v, const T& lo, const T& hi );

template< class T, class Compare >
constexpr const T& clamp( const T& v, const T& lo, const T& hi, Compare comp );

2.2 参数说明

  • v: 要限制的值
  • lo: 下限
  • hi: 上限
  • comp: 可选的比较函数对象

2.3 返回值

  • 如果 v 小于 lo,返回 lo
  • 如果 v 大于 hi,返回 hi
  • 否则,返回 v



3. 基本用法

让我们通过一些简单的例子来了解 std::clamp 的基本用法。

#include <iostream>
#include <algorithm>

int main() {
    std::cout << std::clamp(10, 1, 100) << std::endl;  // 输出: 10
    std::cout << std::clamp(0, 1, 100) << std::endl;   // 输出: 1
    std::cout << std::clamp(1000, 1, 100) << std::endl; // 输出: 100

    double pi = 3.14159;
    std::cout << std::clamp(pi, 3.0, 3.5) << std::endl; // 输出: 3.14159

    return 0;
}



4. 深入理解 std::clamp

4.1 实现原理

std::clamp 的内部实现可能类似于以下代码:

template<class T>
constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
    return (v < lo) ? lo : (hi < v) ? hi : v;
}

这个实现利用了三元运算符的嵌套,使得代码简洁高效。

4.2 注意事项

  1. 参数顺序很重要:必须保证 lo <= hi,否则行为未定义。
  2. std::clamp 返回的是引用,这意味着它不会创建新的对象。
  3. 对于自定义类型,需要确保正确重载了比较运算符。



5. 高级用法

5.1 自定义比较函数

std::clamp 允许我们传入自定义的比较函数,这在处理复杂对象时特别有用。

#include <iostream>
#include <algorithm>
#include <string>

int main() {
    auto cmp = [](const std::string& a, const std::string& b) { return a.length() < b.length(); };
    
    std::string result = std::clamp(std::string("hello"), std::string("a"), std::string("world"), cmp);
    std::cout << result << std::endl;  // 输出: hello

    return 0;
}

5.2 与 lambda 表达式结合

#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 5, 10, 15, 20};
    
    std::for_each(numbers.begin(), numbers.end(), [](int& n) {
        n = std::clamp(n, 5, 15);
    });

    for (int n : numbers) {
        std::cout << n << " ";
    }
    // 输出: 5 5 10 15 15

    return 0;
}



6. 实际应用场景

6.1 图形编程

在图形编程中,std::clamp 常用于限制颜色值或坐标。

struct Color {
    int r, g, b;
    
    void normalize() {
        r = std::clamp(r, 0, 255);
        g = std::clamp(g, 0, 255);
        b = std::clamp(b, 0, 255);
    }
};

6.2 游戏开发

在游戏开发中,std::clamp 可用于限制玩家的生命值、能量等属性。

class Player {
    int health;
public:
    void takeDamage(int damage) {
        health = std::clamp(health - damage, 0, 100);
    }
};

6.3 信号处理,参数读写

在信号处理中,std::clamp 可用于限制信号的幅度。
在参数读写中,std::clamp 可用于保护参数的范围,避免异常数据的写入与读取。

std::vector<double> processSignal(const std::vector<double>& signal, double minAmplitude, double maxAmplitude) {
    std::vector<double> processedSignal;
    for (double sample : signal) {
        processedSignal.push_back(std::clamp(sample, minAmplitude, maxAmplitude));
    }
    return processedSignal;
}

template<typename T>
void writeParam(T data) {
	if constexpr (std::is_unsigned<T>::value) {
        data = std::clamp(v.toUInt(), r.first.toUInt(), r.second.toUInt());
    } else if constexpr (std::is_floating_point<T>::value) {
        data = std::clamp(v.toFloat(), r.first.toFloat(), r.second.toFloat());
    } else {
        data = std::clamp(v.toInt(), r.first.toInt(), r.second.toInt());
    }
    //  写入参数
}



7. std::clamp vs 其他方法


7.1 vs 手动实现

手动实现:

int clampManual(int v, int lo, int hi) {
  if (v < lo) return lo;
   if (v > hi) return hi;
  return v;
}

std::clamp 相比手动实现有以下优势:

  1. 代码更简洁
  2. 泛型实现,可用于任何可比较类型
  3. 编译器可能会对 std::clamp 进行优化

7.2 vs std::min 和 std::max 组合

有时候人们会使用 std::minstd::max 的组合来实现 clamp 功能:
虽然这种方法也能达到目的,但 std::clamp 有以下优点:

  1. 语义更清晰
  2. 可能有更好的性能(取决于编译器优化)
  3. 对于自定义类型,只需要实现 < 运算符,而不是同时需要 < 和 >
int clampUsingMinMax(int v, int lo, int hi) {
 return std::min(std::max(v, lo), hi);
}

7.3 vs Qt库的qBound函数

主要区别是参数顺序不同,qBound更兼容Qt类型的数据。



8. 性能考虑

在大多数情况下,std::clamp 的性能表现excellent。现代编译器能够很好地优化这个函数。然而,在处理大量数据时,我们还是应该进行性能测试。

#include <iostream>
#include <algorithm>
#include <chrono>
#include <vector>
#include <random>

int main() {
    constexpr int SIZE = 10000000;
    std::vector<int> data(SIZE);
    
    // 生成随机数
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(-1000, 1000);
    for (int& n : data) {
        n = dis(gen);
    }

    auto start = std::chrono::high_resolution_clock::now();
    for (int& n : data) {
        n = std::clamp(n, -500, 500);
    }
    auto end = std::chrono::high_resolution_clock::now();

    std::chrono::duration<double, std::milli> elapsed = end - start;
    std::cout << "Time taken: " << elapsed.count() << " ms" << std::endl;

    return 0;
}


9. 常见问题与解决方法

9.1 编译错误: ‘clamp’ is not a member of ‘std’

解决方法:

  1. 确保使用的是 C++17 或更高版本
  2. 检查是否包含了 <algorithm> 头文件

9.2 自定义类型的 clamp 操作失败

解决方法:

  1. 确保自定义类型正确重载了 < 运算符
  2. 考虑使用自定义比较函数的 std::clamp 重载版本

9.3 浮点数精度问题

当使用 std::clamp 处理浮点数时,要注意精度问题:

double result = std::clamp(0.1 + 0.2, 0.3, 0.4);
std::cout << std::setprecision(17) << result << std::endl;
// 可能输出: 0.30000000000000004

解决方法: 使用 epsilon 值进行比较,或考虑使用定点数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2134696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全球安防监控、工业检测摄像机市场规模情况一览

一、全球安防监控市场规模情况综合分析 &#xff08;1&#xff09;全球安防监控摄像机市场规模 全球市场研究公司Research Nester统计&#xff0c;2023年全球安防监控摄像机市场规模为811.1亿元&#xff0c;预测到2028年&#xff0c;全球安全与监控市场规模预计将达到1869.3亿…

将 Parallels Desktop(PD虚拟机)安装在移动硬盘上,有影响吗?

当我们谈论在移动硬盘上安装 Parallels Desktop&#xff08;简称PD虚拟机&#xff09;及其对性能的影响时&#xff0c;特别是在运行如Unigraphics这样的资源密集型软件时&#xff0c;用户需要在便携性与性能之间找到最佳平衡。本文将深入探讨PD虚拟机装在移动硬盘有影响吗&…

(javaweb)mysql---DDL

一.数据模型&#xff0c;数据库操作 1.二维表&#xff1a;有行有列 2. 3.客户端连接数据库&#xff0c;发送sql语句给DBMS&#xff08;数据库管理系统&#xff09;&#xff0c;DBMS创建--以文件夹显示 二.表结构操作--创建 database和schema含义一样。 这样就显示出了之前的内容…

类和对象(中)【上篇】(构造,析构,拷贝函数)

&#x1f31f;个人主页&#xff1a;落叶 目录 类的默认成员函数 构造函数 无参构造 带参构造函数 全缺省构造函数 析构函数 对⽐C和C解决括号匹配问题 C语言版的Stack C版的Stack 拷⻉构造函数 类的默认成员函数 默认成员函数就是⽤⼾没有显式实现&#xff0c;编译器会…

如何查看微信聊天记录,防员工私单(有效监管员工电脑微信聊天的方法)

在企业管理中&#xff0c;防止员工私单&#xff08;即员工绕过公司直接与客户交易&#xff09;是管理中的一大难题。 许多员工使用微信进行日常工作沟通&#xff0c;而如果管理不到位&#xff0c;容易产生私单问题&#xff0c;影响企业的利益。 为了解决这一问题&#xff0c;…

【自费2W真机测评】三款热门/旗舰宠物空气净化器米家、希喂、352对比试用!

我家老大是三个月大的时候接回来的&#xff0c;接回来前就是家教好的小猫咪一只&#xff0c;不乱尿、不掉毛的。看朋友家都被猫咪掉毛困扰着&#xff0c;我还嘚瑟觉得自己养可好了&#xff0c;根本不掉毛。养了三个月老大长成大猫猫了&#xff0c;我又觉得我可以了&#xff0c;…

浏览器插件快速开启/关闭IDM接管下载

假设你已经为浏览器安装了IDM扩展&#xff0c;那么按下图的点击顺序&#xff0c;可以快速开启或关闭IDM的下载接管&#xff0c;而不必在IDM软件的设置->选项中&#xff0c;临时作调整。

再次进阶 舞台王者 第八季完美童模全球赛代言人【孟梓娴】赛场+秀场超燃合集!

7月20-23日&#xff0c;2024第八季完美童模全球总决赛在青岛圆满落幕。在盛大的颁奖典礼上&#xff0c;一位才能出众的少女——孟梓娴迎来了她舞台生涯的璀璨时刻。 代言人——孟梓娴&#xff0c;以璀璨童星之姿&#xff0c;优雅地踏上完美童模盛宴的绚丽舞台&#xff0c;作为开…

Qt-QLabel显示类控件(25)

目录 描述 相关属性介绍 使用 纯文本、富文本、markdown 显示图片 实时修改图片尺寸 文本对齐 Box框 水平 垂直对其 其他的文本对齐 设置伙伴 描述 用来显示文本的&#xff0c;如果看了之前的&#xff0c;那么对这个控件应该不陌生&#xff0c;这里我们就详细介绍…

初步了解python数据分析

首先输入 pip install pandas 安装pandas&#xff0c;安装完成如下&#xff0c; 一个示例&#xff0c; import pandas as pddata {A: [11, 22, 33, 44, 55], B: [51, 4, 33, 2, 1]} df1 pd.DataFrame(data)matrix1 df1.corr() print(matrix1) 输出如下&#xff0c; Pandas …

ppt怎么做的好看?这些技巧让你的演示更上一层楼

当中秋佳节来临&#xff0c;准备一场生动有趣的中秋节科普活动时&#xff0c;一套精美的PPT模板无疑是点睛之笔。它不仅能让你的科普内容更加引人入胜&#xff0c;还能在视觉上给观众留下深刻印象。 为了帮助你快速打造出这样一份PPT&#xff0c;以下是几款优质的ppt模板免费软…

代码管理工具——git及阿里云云效的使用(包含git的使用及云效自动化部署)

1、做项目开发时都会用到代码管理工具,像是我之前使用过gitHub,Visual Studio等一些代码管理工具&#xff0c;这里介绍的是阿里云云效的使用。 2、首先登录阿里云云效&#xff0c;登录进去之后会看到公司给你开放的一个仓库。 3、进入仓库&#xff0c;点击克隆/下载&#xff0…

搜索软件 Everything 的安装与使用教程

一、Everything简介 适用于 Windows 的免费搜索工具 Everything 是 Windows 的即时搜索引擎。发现、整理并轻松访问文件和文件夹&#xff0c;一切尽在指尖&#xff01; PS&#xff1a;Everything无法对文件内容进行搜索&#xff0c;只能根据文件名和路径进行搜索 二、Everyt…

ollama语言大模型部署使用

ollama语言大模型部署使用 前言一、下载安装maxkb1、下载解压赋权2、安装 二、安装ollamadocker运行 三、无需获取api_keymaxkb安装ollama模型对&#xff0c;就是这&#xff0c;你选好基础模型后&#xff0c;只需要给他地址&#xff0c;添加完成后自行调用ollama安装你选择好的…

GD - GDLink的接口引脚杜邦线接触不好,还是自己做一个转接头好些

文章目录 GD - GDLink的接口引脚杜邦线接触不好&#xff0c;还是自己做一个转接头好些概述笔记转接头使用时的连接关系转接头弄个壳子好些在转接头上&#xff0c;将线序转为自己板子SWD的防呆线序 转接头的2x5P线序和看到的GD-LINK2x5P接口相反END GD - GDLink的接口引脚杜邦线…

37拼购:电商新风尚,共享双赢的购物革命

随着2024年电商市场的日益繁荣&#xff0c;商品海洋中的同质化问题愈发严峻&#xff0c;消费者在茫茫商海中寻觅独特价值的难度陡增。在此背景下&#xff0c;一种名为“37悦享拼”的创新电商模式横空出世&#xff0c;它巧妙融合了私域社交与电商精髓&#xff0c;旨在打破传统壁…

Java wrapperr打包springboot项目到linux和Windows

Java wrapper打包springboot项目到linux和Windows 1 Java wrapper 说明2 linux的安装步骤2.1 解压 创建目录2.2 复制文件2.3 配置文件2.4 启动 3 windows3.1 配置文件3.2 复制文件3.3 启动 1 Java wrapper 说明 前提&#xff1a; 一定要有Java环境&#xff08;我使用的是jdk1.8…

【ArcGIS Pro】扩展模块 Nuget 使用

前提需知 ArcGIS Pro 扩展模块30 NuGet 仅适用于 ArcGIS Pro 加载项和配置。由于 NuGet 包含 Pro 公共扩展模块 API&#xff0c;因此不应在核心主机应用程序和 Pro 插件中引用它&#xff0c;这些应用程序和插件旨在与 ArcGIS.Core 地理数据库和几何 API 一起使用&#xff0c;而…

druid jdbc 执行 sql 输出 开销耗时

druid 执行sql输出 参考链接配置_LogFilter alibaba/druid Wiki GitHub 看不太懂的往这里瞅瞅。 1. 别名映射 这个地方 给我们提供了 5 种 logfilter : log4j、log4j2、slf4j、commonlogging和commonLogging 每一种实际上都代表一个日志框架 或 日志门面。 -Ddruid.fil…

吊打面试官!业务架构的关键概念

商业模式 商业模式是帮助企业成功的“秘诀”&#xff0c;它通过整合企业内外部的多种要素&#xff0c;构建起一个全面、高效且具有独特竞争优势的运营体系。这一体系的目的是满足市场的需求&#xff0c;实现各利益相关者价值最大化&#xff0c;并确保企业的长期盈利能力。 商…