我正在测试 C++ 20 的范围,这是我的main.cpp:
#include <ranges>
#include <iostream>
int main()
{
auto const ints = {0,1,2,3,4,5};
auto even = [](int i) { return 0 == i % 2; };
auto square = [](int i) { return i * i; };
for (int i : ints | std::views::filter(even) | std::views::transform(square)) {
std::cout << i << ' ';
}
std::cout << '\n';
}
Run Code Online (Sandbox Code Playgroud)
编译它clang++-12但找不到“范围”:
$ clang++-12 --version
Ubuntu clang version 12.0.1-++20210525082622+328a6ec95532-1~exp1~20210525063352.95
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin …Run Code Online (Sandbox Code Playgroud) 我有一个简单的容器:
template <class T, class Allocator = std::allocator<T>>
class ring
{
public:
using value_type = T;
using allocator_type = Allocator;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = T &;
using const_reference = const T &;
using pointer = T *;
using const_pointer = const T *;
private:
template <class E>
class ring_iterator
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = E;
using difference_type = std::ptrdiff_t;
using reference = E &;
using pointer = E *; …Run Code Online (Sandbox Code Playgroud) C++20 引入了std::common_iterator能够将元素的非公共范围(迭代器和哨兵的类型不同)表示为公共范围(它们相同)的 a ,其概要定义为:
template<input_or_output_iterator I, sentinel_for<I> S>
requires (!same_as<I, S> && copyable<I>)
class common_iterator {
// ...
private:
variant<I, S> v_; // exposition only
};
Run Code Online (Sandbox Code Playgroud)
它对于与期望范围的开始和结束具有相同类型的遗留代码进行交互非常有用。
在[iterators.common#common.iter.types-1.1] 中,其iterator_concept定义为:
iterator_concept表示forward_iterator_tag如果I模型forward_iterator;否则表示input_iterator_tag。
为什么common_iterator最多只能是 a forward_iterator,而不能完全定义其iterator_concept基于I's iterator_category?例如,如果I是random_asscess_iterator,则common_iterator<I, S>是random_asscess_iterator,依此类推。
看来,这是因为技术上是可行的common_iterator只是使用std::variant类型为擦除I和S。
考虑以下(Godbolt): …
有没有比反转两次更好的方法来使用 c++20 范围删除容器中的最后一个元素?
#include <iostream>
#include <vector>
#include <ranges>
int main()
{
std::vector<int> foo{1, 2, 3, 4, 5, 6};
for (const auto& d: foo | std::ranges::views::reverse
| std::ranges::views::drop(1)
| std::ranges::views::reverse)
{
std::cout << d << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud) 有些用法|看起来更像函数管道或链接,而不是按位或,与 c++20 范围结合使用。像:
#include <ranges>
#include <vector>
template<typename T>
std::vector<T> square_vector(const std::vector<T> &some_vector) {
auto result = some_vector | std::views::transform([](T x){ return x*x; };
return {result.begin(), result.end()};
}
Run Code Online (Sandbox Code Playgroud)
显然,该|运算符不是按照通常意义上的按位或来使用的。它从什么时候开始工作,在什么类型的函数/对象上工作?这些像常规视图吗?有哪些注意事项?
我正在尝试将视图创建为转换类型的向量。从文档中我读到以下内容应该有效,但编译器输出非常混乱。我缺少什么?
#include <ranges>
#include <vector>
int main() {
std::vector<int> v {1, 2, 3};
auto view = v | std::views::transform([](int i){ return std::to_string(i); });
}
Run Code Online (Sandbox Code Playgroud)
编译器输出:
In file included from <source>:1:
In file included from /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/ranges:43:
In file included from /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/iterator:61:
In file included from /opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/stl_iterator_base_types.h:71:
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/iterator_concepts.h:980:13: error: no matching function for call to '__begin'
= decltype(ranges::__cust_access::__begin(std::declval<_Tp&>()));
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/ranges_base.h:586:5: note: in instantiation of template type alias '__range_iter_t' requested here
using iterator_t = std::__detail::__range_iter_t<_Tp>;
^
/opt/compiler-explorer/gcc-11.2.0/lib/gcc/x86_64-linux-gnu/11.2.0/../../../../include/c++/11.2.0/bits/ranges_util.h:98:43: note: in instantiation of template type …Run Code Online (Sandbox Code Playgroud) 例如std::ranges::transform_view
范围适配器,表示对每个元素应用转换函数后底层序列的视图。
和std::ranges::transform
将给定函数应用于一个范围,并将结果存储在另一个范围中,从 result 开始。
如果我们想将字符串转换为大写,我们可以同时使用算法和视图:
int main() {
std::string in{ "hello\n" };
std::string out;
// using transform view
std::ranges::copy( std::views::transform(in, toupper), std::back_inserter(out) );
std::cout << out;
out.clear();
// using transform algorithm
std::ranges::transform(in, std::back_inserter(out), ::toupper);
std::cout << out;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
视图能完成哪些算法无法完成的任务,反之亦然?我什么时候应该选择其中一种而不是另一种?
我知道 iota 可能很复杂(例如无限),因此在一般情况下这不容易完成,但在某些情况下应该可以在 O(1) 中进行查找/包含操作。
例如
int main() {
auto vals = views::iota(10'000'000'000, 100'000'000'000);
return ranges::find(vals, 74'656'000'000) != vals.end();
}
Run Code Online (Sandbox Code Playgroud)
“无限”运行(进行线性搜索)
显然检查可以在 O(1) 内完成。
有没有办法用 C++ 来实现这种通用方式(即在其他视图上查找/包含需要线性时间,并且当它检测到 iota 时需要 O(1)),或者我需要手动检测传递给我的函数的视图是什么时候有限iota视图并进行>=front <=back检查?
考虑以下情况:
std::vector<int> v{0, 1, 2, 3, 4, 5};
// 0 1 2 3 4 5
auto rng1 = std::views::all(v);
// 5 4 3 2 1 0
auto rng2 = std::views::reverse(v);
// 4 2 0
auto rng3 = std::views::filter(rng2, [](int x){return x % 2 == 0;});
Run Code Online (Sandbox Code Playgroud)
有没有一种优雅的方法将这三个适配器连接到一个视图中,如下所示:
// 0 1 2 3 4 5 5 4 3 2 1 0 4 2 0
auto final_rng = std::views::concat(rng1, rng2, rng3);
Run Code Online (Sandbox Code Playgroud)
这似乎不可能,因为rng1、rng2、 和rng3是非常不同的类型。
有人可以提供替代解决方案吗?谢谢。
为什么std::range::sort(和其他基于范围的算法)在range命名空间中实现?为什么不将其定义为std::sort范围超载?