string_view
operator+
vs. StrCat()
absl::Status
std::bind
absl::optional
and std::unique_ptr
absl::StrFormat()
make_unique
and private
Constructors.bool
explicit
= delete
)switch
Statements Responsibly= delete
AbslHashValue
and Youcontains()
std::optional
parametersif
and switch
statements with initializersinline
Variablesstd::unique_ptr
Must Be MovedAbslStringify()
vector.at()
auto
for Variable DeclarationsOriginally posted as TotW #231 on March 7, 2024
Updated 2024-09-30
Quicklink: abseil.io/tips/231
In recent C++ versions the Standard Library has added a few functions whose sole
job is to supply some (specific!) point somewhere between two other points x
and y
: std::clamp
(from
C++17), and
std::midpoint
and
std::lerp
(from C++20).
Adding these function templates to the Standard Library serves two main
purposes. First, it establishes common terminology (vocabulary) for these
operations, that is likely to be widely recognized. Second, and particularly in
the case of std::midpoint
and std::lerp
, it ensures the availability of high
quality implementations that
avoid common pitfalls.
All of these operations are constexpr
, meaning that they can be used both at
runtime and at compile time. The types that can be passed to them depend on the
specific operation; they all support floating point types, and std::midpoint
and std::clamp
offer additional flexibility. Read on for the details.
std::clamp
std::clamp(x, min, max)
“clamps” x to the range [min
, max
]. More
explicitly, if x
is already in the range min
to max
(inclusive) then
std::clamp(x, min, max)
returns x
, and for x
outside of that range
std::clamp
returns whichever of min
or max
is closest to x
. This is
equivalent to std::max(std::min(x, max), min)
except for being a more direct
way to express the intent (and much less of a puzzle for readers).
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 tostd::clamp
and binding the result to a temporary:// `std::clamp(1, 3, 4)` returns a reference to a temporary int initialized // from `3`, which must not be used beyond the lifetime of the temporary. // See [Tip #101](/tips/101). const int& dangling = std::clamp(1, 3, 4);
std::clamp
works for any type that can be compared with <
(or with a
user-supplied comparator passed to std::clamp(x, min, max, cmp
).
std::midpoint
There are no surprises in what std::midpoint
does: std::midpoint(x, y)
returns a point halfway between x
and y
(rounding towards x
when x
and
y
are of an integral type).
std::midpoint(x, y)
works for values x
, y
of any floating point or
integral type (not including bool
). As a bonus, std::midpoint(p, q)
also
works for pointers p
, q
into an array.
std::lerp
The lerp
in std::lerp
is short for “linear interpolation”, and std::lerp(x,
y, t)
returns a value some fraction t
of the way from x
to y
. For
example, std::lerp(x, y, 0)
returns x
, std::lerp(x, y, 1)
returns y, and
std::lerp(x, y, 0.5)
can be simplified to std::midpoint(x, y)
.
Note: In spite of the name, std::lerp
can also be used for extrapolation if we
pass a value of t
outside of the range [0, 1]
. For example, std::lerp(100,
101, -2)
evaluates to 98, and std::lerp(100, 101, +2)
is 102.
std::lerp
works on floating point types.
std::midpoint(x, y)
over std::lerp(x, y, 0.5)
when applicable.std::clamp
.