mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
145 lines
5.7 KiB
Markdown
145 lines
5.7 KiB
Markdown
|
# `base::ranges`
|
||
|
|
||
|
This directory aims to implement a C++14 version of the new `std::ranges`
|
||
|
algorithms that were introduced in C++20. These implementations are added to the
|
||
|
`::base::ranges` namespace, and callers can access them by including
|
||
|
[`base/ranges/algorithm.h`](https://source.chromium.org/chromium/chromium/src/+/main:base/ranges/algorithm.h).
|
||
|
|
||
|
## Similarities with C++20:
|
||
|
|
||
|
### Automatically deducing `begin()` and `end()`
|
||
|
As probably one of the most important changes for readability and usability, all
|
||
|
algorithms in `base::ranges` have overloads for ranges of elements, which allow
|
||
|
callers to no longer specify `begin()` and `end()` iterators themselves.
|
||
|
|
||
|
Before:
|
||
|
```c++
|
||
|
bool HasEvens(const std::vector<int>& vec) {
|
||
|
return std::any_of(vec.begin(), vec.end(), [](int i) { return i % 2 == 0; });
|
||
|
}
|
||
|
```
|
||
|
|
||
|
After:
|
||
|
```c++
|
||
|
bool HasEvens(const std::vector<int>& vec) {
|
||
|
return base::ranges::any_of(vec, [](int i) { return i % 2 == 0; });
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Furthermore, these overloads also support binding to temporaries, so that
|
||
|
applying algorithms to return values is easier:
|
||
|
|
||
|
```c++
|
||
|
std::vector<int> GetNums();
|
||
|
```
|
||
|
|
||
|
Before:
|
||
|
|
||
|
```c++
|
||
|
bool HasEvens() {
|
||
|
std::vector<int> nums = GetNums();
|
||
|
return std::any_of(nums.begin(), nums.end(),
|
||
|
[](int i) { return i % 2 == 0; });
|
||
|
}
|
||
|
```
|
||
|
|
||
|
After:
|
||
|
```c++
|
||
|
bool HasEvens() {
|
||
|
return base::ranges::any_of(GetNums(), [](int i) { return i % 2 == 0; });
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Support for Projections
|
||
|
In addition to supporting automatically deducing the `begin()` and `end()`
|
||
|
iterator for ranges, the `base::ranges::` algorithms also support projections,
|
||
|
that can be applied to arguments prior to passing it to supplied transformations
|
||
|
or predicates. This is especially useful when ordering a collection of classes
|
||
|
by a specific data member of the class. Example:
|
||
|
|
||
|
Before:
|
||
|
```cpp
|
||
|
std::sort(suggestions->begin(), suggestions->end(),
|
||
|
[](const autofill::Suggestion& a, const autofill::Suggestion& b) {
|
||
|
return a.match < b.match;
|
||
|
});
|
||
|
```
|
||
|
|
||
|
After:
|
||
|
```cpp
|
||
|
base::ranges::sort(*suggestions, /*comp=*/{}, &autofill::Suggestion::match);
|
||
|
```
|
||
|
|
||
|
Anything that is callable can be used as a projection. This includes
|
||
|
`FunctionObjects` like function pointers or functors, but also pointers to
|
||
|
member function and pointers to data members, as shown above. When not specified
|
||
|
a projection defaults to `base::ranges::identity`, which simply perfectly
|
||
|
forwards its argument.
|
||
|
|
||
|
Projections are supported in both range and iterator-pair overloads of the
|
||
|
`base::ranges::` algorithms, for example `base::ranges::all_of` has the
|
||
|
following signatures:
|
||
|
|
||
|
```cpp
|
||
|
template <typename InputIterator, typename Pred, typename Proj = identity>
|
||
|
bool all_of(InputIterator first, InputIterator last, Pred pred, Proj proj = {});
|
||
|
|
||
|
template <typename Range, typename Pred, typename Proj = identity>
|
||
|
bool all_of(Range&& range, Pred pred, Proj proj = {});
|
||
|
```
|
||
|
|
||
|
## Differences from C++20:
|
||
|
To simplify the implementation of the `base::ranges::` algorithms, they dispatch
|
||
|
to the `std::` algorithms found in C++14. This leads to the following list of
|
||
|
differences from C++20. Since most of these differences are differences in the
|
||
|
library and not in the language, they could be addressed in the future by adding
|
||
|
corresponding implementations.
|
||
|
|
||
|
### Lack of Constraints
|
||
|
Due to the lack of support for concepts in the language, the algorithms in
|
||
|
`base::ranges` do not have the constraints that are present on the algorithms in
|
||
|
`std::ranges`. Instead, they support any type, much like C++14's `std::`
|
||
|
algorithms. In the future this might be addressed by adding corresponding
|
||
|
constraints via SFINAE, should the need arise.
|
||
|
|
||
|
### Lack of Range Primitives
|
||
|
Due to C++14's lack of `std::ranges` concepts like sentinels and other range
|
||
|
primitives, algorithms taking a `[first, last)` pair rather than a complete
|
||
|
range, do not support different types for `first` and `last`. Since they rely on
|
||
|
C++14's implementation, the type must be the same. This could be addressed in
|
||
|
the future by implementing support for sentinel types ourselves.
|
||
|
|
||
|
### Lack of `constexpr`
|
||
|
The `base::ranges` algorithms can only be used in a `constexpr` context when
|
||
|
they call underlying `std::` algorithms that are themselves `constexpr`. Before
|
||
|
C++20, only `std::min`, `std::max` and `std::minmax` are annotated
|
||
|
appropriately, so code like `constexpr bool foo = base::ranges::any_of(...);`
|
||
|
will fail because the compiler will not find a `constexpr std::any_of`. This
|
||
|
could be addressed by either upgrading Chromium's STL to C++20, or implementing
|
||
|
`constexpr` versions of some of these algorithms ourselves.
|
||
|
|
||
|
### Lack of post C++14 algorithms
|
||
|
Since most algorithms in `base::ranges` dispatch to their C++14 equivalent, some
|
||
|
`std::` algorithms that are not present in C++14 have no implementation in
|
||
|
`base::ranges`. This list of algorithms includes the following:
|
||
|
|
||
|
- [`std::sample`](https://en.cppreference.com/w/cpp/algorithm/sample) (added in C++17)
|
||
|
|
||
|
### Return Types
|
||
|
Some of the algorithms in `std::ranges::` have different return types than their
|
||
|
equivalent in `std::`. For example, while `std::for_each` returns the passed-in
|
||
|
`Function`, `std::ranges::for_each` returns a `std::ranges::for_each_result`,
|
||
|
consisting of the `last` iterator and the function.
|
||
|
|
||
|
In the cases where the return type differs, `base::ranges::` algorithms will
|
||
|
continue to return the old return type.
|
||
|
|
||
|
### No blocking of ADL
|
||
|
The algorithms defined in `std::ranges` are not found by ADL, and inhibit ADL
|
||
|
when found by [unqualified name lookup][1]. This is done to be able to enforce
|
||
|
the constraints specified by those algorithms and commonly implemented by using
|
||
|
function objects instead of regular functions. Since we don't support
|
||
|
constrained algorithms yet, we don't implement the blocking of ADL either.
|
||
|
|
||
|
[1]: https://wg21.link/algorithms.requirements#2
|