以下代码未能通过静态断言:
#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之前编写的自定义容器来满足要求。
首先,请注意,每个[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 cbegin和cend。让我们看看相关部分的内容:
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。