std::ranges::begin 和 std::begin 有什么区别?

SWd*_*WdV 11 c++ range c++20 std-ranges

std::begin和 new 和有什么不一样std::ranges::begin?(同样为endsize等等)

两者似乎工作相同:

#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);
}
Run Code Online (Sandbox Code Playgroud)

1111按预期编译和打印。

难道ranges::beginstd::begin过时了吗?或者两者有不同的用例吗?

Bar*_*rry 22

有一些区别。

首先,ranges::begin(x)适用于所有范围,std::begin(x)而不适用于所有范围。后者不会对 进行 ADL 查找begin,因此指定的范围如下:

struct R {
    ...
};
auto begin(R const&);
auto end(R const&);
Run Code Online (Sandbox Code Playgroud)

不会工作,这就是为什么你必须写这样的东西:

using std::begin, std::end;
auto it = begin(r);
Run Code Online (Sandbox Code Playgroud)

您不必对ranges::begin.

其次,ranges::begin(x)安全一点。Ranges 引入了借用 range 的概念,这是一个可以安全保留其迭代器的范围。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
Run Code Online (Sandbox Code Playgroud)

第三,ranges::beginranges::end有额外的类型检查。ranges::begin(r)需要r.begin()or begin(r)to model的结果input_or_output_iteratorranges::end(r)需要ranges::begin(r)有效并且需要r.end()end(r)对模型sentinel_for<decltype(ranges::begin(r))>. 这是- ,无论我们从得到beginend真正的范围。

这意味着,例如:

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
Run Code Online (Sandbox Code Playgroud)

虽然更烦人的是你有一个迭代器类型,它可能是可递增的、可取消引用的、可比较的等等......但没有默认的构造函数。这不符合 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?
Run Code Online (Sandbox Code Playgroud)

第五,一些范围自定义点对象除了只调用该名称的函数之外还有其他回退行为。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) == 0ranges::begin(r) == ranges::end(r)