为什么std :: cbegin()不对容器调用.cbegin()?

Tri*_*ock 16 c++

以下代码未能通过静态断言:

#include <gsl/span>
#include <iterator>
#include <type_traits>

int main()
{
    int theArr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    gsl::span<int> theSpan{ theArr, std::size(theArr) };

    using std::cbegin;
    auto it1 = cbegin(theSpan);
    auto it2 = theSpan.cbegin();
    static_assert(std::is_same_v<decltype(it1), decltype(it2)>);
}
Run Code Online (Sandbox Code Playgroud)

之所以失败,是因为在容器的const ref上std::cbegin()调用了该.begin()方法。对于标准定义的容器,这将返回const_iterator,与.cbegin()返回的类型相同。但是,gsl::span它有点独特,因为它模拟了一种“借贷类型”。A的const gsl::span行为类似于const指针;span本身是const,但指向的不是const。因此,still .begin()上的方法const gsl::span仍返回非常量迭代器,而显式调用.cbegin()将返回常量迭代器。

我很好奇为什么std::cbegin()不定义为调用.cbegin()容器(所有标准容器似乎都在实现)来解决此类情况。

这与以下方面有关:std :: cbegin为什么返回与std :: begin相同的类型

eer*_*ika 12

这失败了,因为std::cbegin()调用了.begin()

更精确地说,是std::cbegin调用std::begin,它在通用重载中调用c.begin

对于它的价值,它应该是可以修复gsl::span后返回常量迭代std::cbegin如果GSL的设计师指定不存在了的泛型重载一个专门std::cbegin用于gsl::span该用途c.cbegin的替代std::begin,如果是这样期望的行为。我不知道他们不指定此类专业的理由。

至于为什么std::cbegin使用的原因std::begin,我也不知道,但是它的确具有能够支持具有c.begin成员但没有c.cbegin成员的容器的优点,这可以被视为不太严格的要求,因为它可以在没有约定提供c.cbegin成员函数的情况下,可以用C ++ 11之前编写的自定义容器来满足要求。


L. *_* F. 8

首先,请注意,每个[tab:container.req]

表达: a.cbegin()

返回类型: const_­iterator

操作语义: const_­cast<?X const&?>(a)?.begin();

复杂度:恒定

因此,gsl::span根本不是容器。 cbegin并且cend被设计成与容器的工作。有一些例外情况(arrays initializer_list)需要特别注意,但显然标准库不能提及gsl::span

其次,是LWG 2128引入了global cbegincend。让我们看看相关部分的内容:

std::cbegin/cend()通过调用实现std::begin/end()。这具有许多优点:

  • 它自动处理数组,这是这些非成员函数的重点。

  • 它可以与C ++ 98/03时代的用户容器一起使用,这些容器是在cbegin/cend()发明成员之前编写的。

  • 它可与一起使用initializer_list,这极少且没有cbegin/cend()成员。

  • [container.requirements.general]保证这等同于调用cbegin/cend()成员。

从本质上讲,调用std::begin/end()省去了为数组和提供特殊照顾的工作initializer_list