-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #492 from manshreck/master
Export of internal doc changes to C++ Tips:
- Loading branch information
Showing
4 changed files
with
650 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
--- | ||
title: "Tip of the Week #231: Between Here and There: Some Minor Overlooked Algorithms" | ||
layout: tips | ||
sidenav: side-nav-tips.html | ||
published: true | ||
permalink: tips/231 | ||
type: markdown | ||
order: "231" | ||
--- | ||
|
||
Originally posted as TotW #231 on March 7, 2024 | ||
|
||
*By [James Dennett](mailto:[email protected])* | ||
|
||
Updated 2024-09-30 | ||
|
||
Quicklink: [abseil.io/tips/231](https://abseil.io/tips/231) | ||
|
||
|
||
## Overview | ||
|
||
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`](https://en.cppreference.com/w/cpp/algorithm/clamp) (from | ||
C++17), and | ||
[`std::midpoint`](https://en.cppreference.com/w/cpp/numeric/midpoint) and | ||
[`std::lerp`](https://en.cppreference.com/w/cpp/numeric/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](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0811r3.html). | ||
|
||
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. | ||
|
||
### <code>std::clamp</code> | ||
|
||
`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 to | ||
> `std::clamp` and binding the result to a temporary: | ||
> | ||
> <pre class="prettyprint lang-cpp bad-code"> | ||
> // `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); | ||
> </pre> | ||
`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`). | ||
|
||
### <code>std::midpoint</code> | ||
|
||
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. | ||
|
||
### <code>std::lerp</code> | ||
|
||
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. | ||
|
||
## Recommendations | ||
|
||
1. One of the main benefits of these library functions is that they provide a | ||
common vocabulary. As always, prefer to use these standard facilities | ||
instead of writing them from scratch. | ||
1. Prefer `std::midpoint(x, y)` over `std::lerp(x, y, 0.5)` when applicable. | ||
1. Avoid declaring a reference to the result of `std::clamp`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
--- | ||
title: "Tip of the Week #232: When to Use <code>auto</code> for Variable Declarations" | ||
layout: tips | ||
sidenav: side-nav-tips.html | ||
published: true | ||
permalink: tips/232 | ||
type: markdown | ||
order: "232" | ||
--- | ||
|
||
Originally posted as TotW #232 on June 20, 2024 | ||
|
||
*By [Kenji Inoue](mailto:[email protected]) and Michael Diamond, Google Engineer* | ||
|
||
Updated 2024-09-30 | ||
|
||
Quicklink: [abseil.io/tips/232](https://abseil.io/tips/232) | ||
|
||
|
||
The style guide says in the | ||
[Type Deduction (including auto)](https://google.github.io/styleguide/cppguide.html#Type_deduction) | ||
section: | ||
|
||
> Use type deduction only if it makes the code clearer to readers who aren't | ||
> familiar with the project, or if it makes the code safer. Do not use it merely | ||
> to avoid the inconvenience of writing an explicit type. | ||
Ironically, overuse of `auto` often leads to code becoming less clear. Over | ||
time, however, several patterns have emerged where using `auto` can improve code | ||
clarity and safety, such as: | ||
|
||
* In situations where specifying the type correctly can be difficult and | ||
specifying the wrong type can lead to performance or correctness issues, | ||
e.g., range-based `for` loops over a map. | ||
* In situations where the type information is truly redundant and | ||
specification of the full type is distracting, e.g., commonly-used templated | ||
factory functions and some iterator uses. | ||
* In generic code where the type itself is not important as long as it is | ||
syntactically correct. | ||
|
||
We'll discuss each of those cases below, with an eye toward clarifying the cases | ||
in which `auto` makes code safer or clearer. | ||
|
||
## Range-Based For Loops Over a Map | ||
|
||
The following code has a problem that each element in the map is unintentionally | ||
copied: | ||
|
||
<pre class="prettyprint lang-cpp bad-code"> | ||
absl::flat_hash_map<std::string, DogBreed> dog_breeds_by_name = ...; | ||
// `name_and_breed` is copy-constructed for each element of the map. | ||
for (const std::pair<std::string, DogBreed>& name_and_breed : | ||
dog_breeds_by_name) { | ||
... | ||
} | ||
</pre> | ||
|
||
The unintended copy happens because the `value_type` of associative containers | ||
is `std::pair<const Key, Value>` and `std::pair` allows implicit conversions | ||
between pair objects if their underlying types can be implicitly converted. | ||
Because `std::pair::first_type` here is `std::string` and the map entry here has | ||
`std::pair::first_type` of `const std::string`, the pairs are not the same type | ||
and an implicit conversion occurs, copying the contents of the pair despite | ||
`name_and_breed` being declared as a reference. | ||
|
||
Using `auto`, possibly in conjunction with structured bindings | ||
([Tip #169](/tips/169)), can make the code safer and more performant: | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
absl::flat_hash_map<std::string, DogBreed> dog_breeds_by_name = ...; | ||
|
||
// `auto` with structured bindings - if the element types are clear from local | ||
// context. | ||
for (const auto& [name, breed] : dog_breeds_by_name) { | ||
... | ||
} | ||
</pre> | ||
|
||
Sometimes, the element types are not obvious from local context. In that case, | ||
you can do this: | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
// `auto` without structured bindings - allows specifying the element types. | ||
for (const auto& name_and_breed : dog_breeds_by_name) { | ||
const std::string& name = name_and_breed.first; | ||
const DogBreed& breed = name_and_breed.second; | ||
... | ||
} | ||
</pre> | ||
|
||
## Iterators | ||
|
||
The names of iterator types are verbose and often provide redundant type | ||
information when the type of the container is visible nearby. | ||
|
||
Here is an example code snippet that assigns an iterator to a local variable. | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
std::vector<std::string> names = ...; | ||
std::vector<std::string>::iterator name_it = names.begin(); | ||
while (name_it != names.end()) { | ||
... | ||
} | ||
</pre> | ||
|
||
All containers expose `begin()` and `end()` functions which return iterators, | ||
and these iterators have type `ContainerType::iterator` or | ||
`ContainerType::const_iterator`. | ||
|
||
When the type of the container is visible nearby, calling out these types would | ||
only have a small benefit of differentiating `iterator` and `const_iterator` | ||
because the container type part (e.g., `std::vector<std::string>`) is the same | ||
as that of the container. In this case, we can use `auto` to remove redundancy | ||
without hiding helpful information: | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
std::vector<std::string> names = ...; | ||
auto name_it = names.begin(); | ||
while (name_it != names.end()) { | ||
... | ||
} | ||
</pre> | ||
|
||
When the container type is not visible locally, prefer to spell out the full | ||
iterator type or element type: | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
std::vector<std::string>::iterator name_it = names_.begin(); | ||
while (name_it != names_.end()) { | ||
... | ||
} | ||
</pre> | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
auto name_it = names_.begin(); | ||
while (name_it != names_.end()) { | ||
const std::string& name = *name_it; | ||
... | ||
} | ||
</pre> | ||
|
||
## <code>std::make_unique</code> and Other Google-wide Factory Functions | ||
|
||
In the following code snippet, `std::make_unique` and | ||
`proto2::MakeArenaSafeUnique` specify the types to be instantiated. | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
std::unique_ptr<MyFavoriteType> my_type = | ||
std::make_unique<MyFavoriteType>(...); | ||
|
||
proto2::ArenaSafeUniquePtr<MyFavoriteProto> my_proto = | ||
proto2::MakeArenaSafeUnique<MyFavoriteProto>(arena); | ||
</pre> | ||
|
||
It is widely known throughout Google that `std::make_unique<T>` returns | ||
`std::unique_ptr<T>` and `proto2::MakeArenaSafeUnique<T>` returns | ||
`proto2::ArenaSafeUniquePtr<T>`. In particular, the important part of the | ||
resulting type `T` is specified on the right-hand side (RHS) expression, and it | ||
is company-wide knowledge rather than project-specific knowledge. We can use | ||
`auto` here to remove redundancy without hiding helpful information: | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
auto my_type = std::make_unique<MyFavoriteType>(...); | ||
|
||
auto my_proto = proto2::MakeArenaSafeUnique<MyFavoriteProto>(arena); | ||
</pre> | ||
|
||
## Generic Code | ||
|
||
In some circumstances when writing generic code, such as templates or GoogleTest | ||
matchers, the type may be impossible or very difficult to specify (e.g., a type | ||
written with template metaprogramming or `decltype`). In these cases `auto` may | ||
also be appropriate. However, these situations should be rare. | ||
|
||
## Otherwise: Avoid Using <code>auto</code> | ||
|
||
While it can be tempting to use `auto` in situations where the type is long and | ||
seems obvious to *you*, remember that future readers of the code may not be | ||
familiar with your project and the types it uses | ||
([why](http://go/readability#why)). For example, consider a common pattern of | ||
nested proto access. | ||
|
||
<pre class="prettyprint lang-cpp bad-code"> | ||
// Of course `breed` has type `const DetailedDomesticCatBreed&`! | ||
const auto& breed = cat.pedigree().detailed_breed(); | ||
</pre> | ||
|
||
`auto` may also hide basic semantics like constness, whether a type is a | ||
pointer, and whether a copy is being made ([Tip #44](/tips/44)). | ||
|
||
<pre class="prettyprint lang-cpp bad-code"> | ||
// Did the author mean to make a copy here? | ||
// It is not obvious to all readers that `breed` is not a reference even though | ||
// `detailed_breed()` returns a reference! | ||
auto breed = cat.pedigree().detailed_breed(); | ||
</pre> | ||
|
||
<pre class="prettyprint lang-cpp code"> | ||
// Type and semantics are clear. | ||
const DetailedDomesticCatBreed& breed = cat.pedigree().detailed_breed(); | ||
</pre> | ||
|
||
## Summary of Recommendations | ||
|
||
* Use `auto` when manually writing out a more specific type would incur a high | ||
risk of correctness or performance problems. | ||
* Use `auto` to remove redundancy without hiding helpful information when the | ||
useful type information is visible locally. | ||
* For some generic code where the type is impossible or very difficult to | ||
specify, `auto` may be appropriate; these situations should be rare. | ||
* **Avoid using `auto` in other situations**: while it may make it easier for | ||
you to write the code or allow you to avoid a line-break, it probably makes | ||
the code harder to understand for someone unfamiliar with your project. | ||
|
||
## See Also | ||
|
||
* https://google.github.io/styleguide/cppguide.html#auto for the authoritative | ||
guidance | ||
* [Tip #4](/tips/4): Tip of the Week #4: Automatic for the People | ||
* [Tip #44](/tips/44): Tip of the Week #44: Qualifying auto |
Oops, something went wrong.