(原文链接:https://abseil.io/tips/231 译者:clangpp@gmail.com)
每周贴士 #231: 少用Flags,尤其是在库代码中
- 最初发布于:2024-03-07
- 作者:James Dennett
- 更新于:2024-09-30
- 短链接:abseil.io/tips/231
概述
最近的一些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
,std::clamp
返回min
或max
中距x
最近的那个值。这个操作等价于std::max(std::min(x, max), min)
,只是更直接地表达目的(也少了很多给读者的谜语代码)。
警告:虽然std::clamp
返回引用,但是依赖此行为的代码是隐晦和不常见的,而且需要给读者一个注释作为警报。传给std::clamp
一个临时变量并且绑定返回值到一个临时变量,很容易不小心创建一个悬挂引用:
// `std::clamp(1, 3, 4)`返回一个指向临时变量的引用,该临时变量初始化自`3`,不应该超越该临时变量的生命周期使用。
// 参阅 See [Tip #101](https://abseil.io/tips/101).
const int& dangling = std::clamp(1, 3, 4);
std::clamp
适用于任何可以用<
比较大小的类型(或传一个用户提供的比较器std::clamp(x, min, max, cmp)
)。
Warning: While std::clamp returns a reference, code depending on that is subtle and unusual, and would warrant a comment to alert readers. It is easy to accidentally create a dangling reference by passing a temporary to std::clamp and binding the result to a temporary:
std::midpoint
std::midpoint
干的事儿没什么惊喜:std::midpoint(x, y)
返回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)
。
注:别看它的起名(译者注:叫内插(interpolation)),当输入t
超过[0, 1]
区间的时候,std::lerp
还可以用作外插(extrapolation)。例如,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
的返回值。