本周小贴士#231:来去之间:一些容易忽视的算法

作为TotW#231最初发表再2024年3月7日
作者:James Dennett

概述

在最近的 C++ 版本中,标准库新增了一些专用函数,用于在两个点 x 和 y 之间提供某个(特定的!)点:std::clamp(C++17 引入),以及 std::midpoint 和 std::lerp(C++20 引入)。

将这些函数模板添加到标准库有两个主要目的:

建立通用术语(词汇),便于广泛识别这些操作的含义。
提供高质量实现,尤其是 std::midpoint 和 std::lerp,以避免常见陷阱。
所有这些操作都是 constexpr 的,这意味着它们既可在运行时使用,也可在编译时使用。可传递给这些函数的类型取决于具体操作;它们都支持浮点类型,同时 std::midpoint 和 std::clamp 还具有额外的灵活性。以下是详细信息。

std::clamp

std::clamp(x, min, max) 将 x 限制在范围 [min, max] 内。更具体地说,如果 x 已在 min 到 max 范围内(包含边界),则 std::clamp(x, min, max) 返回 x;如果 x 超出该范围,则返回 min 或 max 中距离 x 最近的值。这相当于 std::max(std::min(x, max), min),但表达意图更直接,也更易于读者理解。

注意:尽管 std::clamp 返回的是引用,但依赖这一特性是微妙且不常见的,代码中应添加注释以提醒读者。如果将临时值传递给 std::clamp 并将结果绑定到引用,很容易意外创建悬空引用:

// `std::clamp(1, 3, 4)` 返回对从 `3` 初始化的临时变量的引用,
// 该引用在临时变量生命周期结束后不得使用。
// 参见 [技巧 #101](/tips/101)。
const int& dangling = std::clamp(1, 3, 4);

std::clamp 适用于任何可以用 < 比较的类型(或通过用户提供的比较器 std::clamp(x, min, max, cmp))。

std::midpoint

std::midpoint 的功能没有意外:std::midpoint(x, y) 返回 x 和 y 的中点(对于整型,向 x 的方向舍入)。

std::midpoint(x, y) 适用于任何浮点或整型值(不包括 bool)。此外,std::midpoint(p, q) 也适用于指向同一数组的指针 p 和 q。

std::lerp

std::lerp 中的 lerp 是“线性插值”(linear interpolation)的缩写,std::lerp(x, y, t) 返回从 x 到 y 的某个分数 t 的值。例如:

std::lerp(x, y, 0) 返回 x,
std::lerp(x, y, 1) 返回 y,
std::lerp(x, y, 0.5) 可简化为 std::midpoint(x, y)。
注意:尽管名称中包含“插值”,但若传入 t 的值超出范围 [0, 1],std::lerp 也可用于外推。例如:

std::lerp(100, 101, -2) 的结果为 98,
std::lerp(100, 101, +2) 的结果为 102。
std::lerp 适用于浮点类型。

建议

这些库函数的主要优势之一是提供了通用术语。因此,优先使用这些标准功能,而不是重新实现。
在适用时,优先使用 std::midpoint(x, y) 而非 std::lerp(x, y, 0.5)。
避免将 std::clamp 的结果声明为引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值