std::ranges::begin 和 std::begin 有什么区别?
What is the difference between std::ranges::begin and std::begin?
std::begin
和新std::ranges::begin
有什么区别?(与end
、size
等相同(
两者的工作方式似乎相同:
#include <iostream>
#include <vector>
#include <array>
#include <ranges>
template<std::ranges::range R>
void printInfo(const R &range)
{
std::cout << (std::ranges::begin(range) == std::begin(range));
}
template<class T>
struct X
{
std::vector<T> v;
auto begin() const { return v.begin(); }
auto end() const { return v.end(); }
};
int main()
{
printInfo(std::vector{1, 2, 3, 4});
printInfo(std::array{1, 2, 3, 4});
printInfo(X<int>{{1, 2, 3, 4}});
int oldSchool[]{1, 2, 3, 4};
printInfo(oldSchool);
}
编译和打印1111
,如预期的那样。
ranges::begin
会让std::begin
过时吗?还是两者有不同的用例?
有一些区别。
首先,ranges::begin(x)
适用于所有范围,而std::begin(x)
则不适用于。后者不会对begin
进行ADL查找,因此指定的范围如下:
struct R {
...
};
auto begin(R const&);
auto end(R const&);
行不通,这就是为什么你必须写这样的东西:
using std::begin, std::end;
auto it = begin(r);
您不必用ranges::begin
执行两步操作。
其次,ranges::begin(x)
更安全一些。范围引入了借用范围的概念,这是一个可以安全地保留其迭代器的范围。 例如,vector<int>
不是借用的范围 - 因为一旦vector
死亡,数据就会死亡。ranges::begin
防范这一点:
auto get_data() -> std::vector<int>;
auto a = std::begin(get_data()); // ok, but now we have a dangling iterator
auto b = ranges::begin(get_data()); // ill-formed
第三,ranges::begin
和ranges::end
有额外的类型检查。ranges::begin(r)
需要r.begin()
或begin(r)
的结果来建模input_or_output_iterator
。ranges::end(r)
要求ranges::begin(r)
有效,并且需要r.end()
或end(r)
才能对sentinel_for<decltype(ranges::begin(r))>
进行建模。也就是说,无论我们从begin
和end
中得到什么,实际上都是一个范围。
这意味着,例如:
struct X {
int begin() const { return 42; }
};
X x;
auto a = std::begin(x); // ok, a == 42
auto b = ranges::begin(x); // ill-formed, int is not an iterator
虽然更烦人的是,你有一个迭代器类型,它可能是可增量的、可取消引用的、可比较的等......但没有默认的构造函数。这不符合C++20input_or_output_iterator
的要求,因此ranges::begin
将失败。
第四,ranges::begin
是一个函数对象,而std::begin
是一组重载的函数模板:
auto f = ranges::begin; // ok
auto g = std::begin; // error: which std::begin did you want?
第五,某些范围自定义点对象除了调用该名称的函数外,还具有其他回退行为。std::size(r)
始终调用名为size
的函数(除非r
是原始数组(。std::empty(r)
总是调用一个名为empty
的函数(除非r
是一个原始数组,在这种情况下它只是false
,或者r
是一个initializer_list
,在这种情况下r.size() == 0
(。但是ranges::size
在某些情况下可以执行ranges::end(r) - ranges::begin(r)
(如果size(r)
和r.size()
不存在,则作为后备(,就像ranges::empty
在某些情况下可以执行ranges::size(r) == 0
或ranges::begin(r) == ranges::end(r)
一样。
- 为constchar*定义std::begin合法吗
- std::ranges::begin 和 std::begin 有什么区别?
- std::begin-类型特征中未考虑用户定义的重载
- 当从成员类调用封装的std::begin时,程序崩溃
- 为什么std::begin()和std::end()适用于固定数组,而不适用于动态数组
- 重载 std::begin() 和 std::end() 用于非数组
- std::begin() 和 std::end() 不适用于类中的未知长度数组
- 将模式包装为 std::begin; 返回 begin(c); 到函数中
- 为什么在这种情况下"std::begin()"总是返回"const_iterator"?
- std::end and std::begin in a function (C++)
- 成员函数 .begin() 和 std::begin()
- 为什么std::cbegin返回与std::begin相同的类型
- 在 C++ 中使用 std::begin(collection) vs collection.begin()
- 依靠 ADL 实现 std::begin() 和 std::end()
- SFINAE problems with std::begin
- 使用 std::begin 和 std::end 进行向量初始化
- 使用 std::begin() 和 std::end() 从数组构造向量
- 我可以专门使用 std::begin 和 std::end 来获取 equal_range() 的返回值吗?
- std::begin() 如何用于内置类型
- std::begin 和 std::end 不使用指针并参考原因