有一些区别。
首先,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) 更安全一些。 Ranges 引入了借用范围 的概念,这是一个您可以安全地持有其迭代器的范围。 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++20 的 input_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 可以under certain circumstances 执行ranges::end(r) - ranges::begin(r)(如果size(r) 和r.size() 不存在则作为后备)就像ranges::empty 可以under certain circumstances 执行ranges::size(r) == 0 或ranges::begin(r) == ranges::end(r)。