我有 Xcode 14 beta,我尝试从 cppreference.com编译这个连接示例。
#include <iostream>
#include <ranges>
#include <string_view>
#include <vector>
int main()
{
using namespace std::literals;
const auto bits = { "https:"sv, "//"sv, "cppreference"sv, "."sv, "com"sv };
for (char const c : bits | std::views::join) std::cout << c; // Error 1
std::cout << '\n';
const std::vector<std::vector<int>> v{ {1,2}, {3,4,5}, {6}, {7,8,9} };
auto jv = std::ranges::join_view(v); // Error 2
for (int const e : jv) std::cout << e << ' ';
std::cout << '\n';
}
Run Code Online (Sandbox Code Playgroud)
我收到错误: …
std::begin.begin如果参数有成员函数,则可以调用该成员函数。似乎也std::ranges::begin做同样的事情。
但是,在 的 页面上,std::ranges::contains我没有看到提及member一词,也没有看到.contains. 这是为什么?
我的意思是,如果我要写,std::ranges::contains(someRange, someVal)我真的希望它会导致对 member 的调用contains,如果someRange碰巧是 astd::map和someVala key 。
我还查看了ranges::containsRange-v3,在我看来,它也只是在范围内进行线性搜索,对支持 member 的参数没有特殊处理contains。
#include <iostream>
#include <ranges>
int main()
{
for (int num: std::ranges::views::iota(0,5)) {
std::cout << num << 'n';
}
for (int i : std::ranges::iota_view{55, 65}) {
std::cout << i << '\n';
}
}
Run Code Online (Sandbox Code Playgroud)
views::iota(e)对于任何合适的子表达式和 ,和views::iota(e, f)分别与和表达式等价。iota_view(e)iota_view(e, f)ef
是否有理由使用其中一种与另一种?
如果不是,既然它们都是在 C++20 中引入的,为什么还要存在呢?
如何在 C++23 中迭代 2 个容器的“枚举 zip”?
在Python中,我会写这样的东西:
A = [10, 20, 30]
B = [40, 50, 60]
for (i, (a, b)) in enumerate(zip(A, B)):
print(i, a, b)
Run Code Online (Sandbox Code Playgroud)
它依次输出和中的索引i和相应元素。AB
在 C++23 中enumerate,zip存在等效项 ( <ranges>),但我不知道如何正确组合它们。
using namespace std;
auto A = vector {10, 20, 30};
auto B = vector {40, 50, 60};
// zip
for (auto [a, b]: views::zip(A, B))
cout << a << " " << b << "\n";
// enumerate
for …Run Code Online (Sandbox Code Playgroud) 我遇到了这种奇怪的行为,如果您创建一个列表的分块视图(由 2 个元素组成),然后尝试在范围 for 循环中打印正面和背面,则前面的方法将起作用,但后面的方法将不起作用t。它将产生一个编译器错误,指出实例化 .back() 方法需要双向迭代器。我错过了什么吗?
重现错误的代码
#include <iostream>
#include <list>
#include <ranges>
#include <vector>
int main() {
std::list<int> nums_list = {0, 1, 2, 3, 4, 5};
auto rng = nums_list | std::views::chunk(2);
for (auto pair : rng) {
std::cout << pair.front(); // Front will work normally
std::cout << pair.back(); // No matching member function for 'back'.
}
}
Run Code Online (Sandbox Code Playgroud)
我也用矢量尝试过,它按预期工作。
我在理解类型何时推导出std::ranges::dangling何时使用命名局部变量与纯右值作为std::ranges::sort算法的参数时遇到问题。例如,我有一个函数将 a 返回prvalue 到标准容器,比如 std::vector ,我直接将其用作 的参数std::ranges::sort,然后我期望std::ranges::dangling在尝试取消引用迭代器时收到编译错误,这就是我得到的:
#include <vector>
#include <algorithm>
auto get_data(){
return std::vector<int>{1, 2, 99, 5, 9, 4};
}
auto get_sorted(){
return std::ranges::sort(get_data());
}
int main(){
auto it = get_sorted();
*(it-1); //Compiler Error because it is a std::ranges::dangling iterator
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我稍微更改get_sorted上面的函数以首先捕获命名变量中的向量并使用它来返回 的结果std::ranges::sort,那么我不会得到悬空迭代器,即使main命名变量中分配的向量应该是函数get_sorted返回后销毁:
auto get_sorted(){
auto vec = get_data();
return std::ranges::sort(vec);
}
int main(){
auto it = get_sorted();
*(it-1); //Okay result = …Run Code Online (Sandbox Code Playgroud) C++20 引入了ranges::borrowed_range,它定义了范围的要求,以便函数可以按值获取它并返回从中获得的迭代器,而不会出现悬空的危险。简而言之(参考P2017R1):
当范围超出范围后您可以保留其迭代器时,该范围是借用范围。
同时,borrowed_subrange_t还引入了类型助手:
template<ranges::range R>
using borrowed_subrange_t = std::conditional_t<
ranges::borrowed_range<R>,
ranges::subrange<ranges::iterator_t<R>>,
ranges::dangling
>;
Run Code Online (Sandbox Code Playgroud)
这是一个别名模板,由一些受限算法(例如ranges::unique和 )使用ranges::find_end,以避免返回潜在的悬空迭代器或视图。
当类型为Rmodels时borrowed_range,borrowed_subrange_tofR基本上是 a subrange<ranges::iterator_t<R>>,这意味着它也是 a ranges::common_range,因为它只接受一个模板参数,并且第二个默认与第一个参数的类型相同。
但似乎存在一些误导,因为有些subrange类型可以借用但仍然是 not common_range,请考虑以下代码:
auto r = views::iota(0);
auto s1 = ranges::subrange{r.begin(), r.begin() + 5};
auto s2 = ranges::subrange{r.begin() + 5, r.end()};
Run Code Online (Sandbox Code Playgroud)
我subrange从 a 创建两个 s borrowed_range ranges::iota_view,一个包含前 5 个元素,另一个包含itoa_view从第五个元素开始的所有元素。它们是subrange的 …
我很难理解 c++20 范围与老式迭代器相比增加了什么。是的,我想不再需要使用beginand end,而是简单的重载,例如:
namespace std {
template <typename Container>
auto transform(Container&& container, auto&&... args) requires ( requires {container.begin(); container.end(); }) {
return transform(container.begin(), container.end(), args...);
}
}
Run Code Online (Sandbox Code Playgroud)
会解决这个问题。
为什么范围很有用?与迭代器相比,我什么时候应该使用它们?
编辑:我知道范围比迭代器还有其他优点(链接、更好的方法等......)。然而,这些(我认为?)都可以用迭代器来完成,我不明白为什么需要引入一个全新的概念,比如范围。
C++20 introduced ranges::elements_view, which accepts a view of tuple-like values, and issues a view with a value-type of the Nth element of the adapted view's value-type, where N is the non-type template parameter.
In [range.elements.view], the synopsis of ranges::elements_view is defined as:
template<input_range V, size_t N>
requires view<V> && has-tuple-element<range_value_t<V>, N> &&
has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
returnable-element<range_reference_t<V>, N>
class elements_view : public view_interface<elements_view<V, N>> {
public:
elements_view() = default;
constexpr explicit elements_view(V base);
// ...
};
Run Code Online (Sandbox Code Playgroud)
Since …
我正在尝试使用其insert(const_iterator pos, InputIt first, InputIt last)成员函数模板将转换后的目录条目范围插入向量中。不幸的是,我无法在 GCC 下编译以下代码,11.1.0根据https://en.cppreference.com/w/cpp/compiler_support应该有范围支持。
#include <filesystem>
#include <vector>
#include <ranges>
#include <iterator>
namespace fs = std::filesystem;
namespace ranges = std::ranges;
namespace views = std::views;
// no solution
namespace std {
template <typename F>
struct iterator_traits<ranges::transform_view<ranges::ref_view<fs::directory_iterator>, F>> {
using iterator_category = std::input_iterator_tag;
};
}
int main() {
std::vector<fs::path> directory_tree;
auto subdir = fs::directory_iterator(".");
ranges::input_range auto subdir_names = subdir
| views::transform([](const auto& entry) { return entry.path(); /* can be more complex*/ }) …Run Code Online (Sandbox Code Playgroud)