既然有了cbegin(), cend(),为什么没有cfront(), cback(), cfind(),…

Given cbegin(), cend(), why is there no cfront(), cback(), cfind(), ...?

本文关键字:cfront cback cfind cend cbegin 为什么      更新时间:2023-10-16

所以,为了允许像

这样的代码
auto vect = ...;
auto it = vect.begin(), end = vect.end(); // want const_iterator, getting iterator

选择正确的begin()end()的重载,即使对于非const容器,也添加了更显式的cbegin()/cend()函数。

为什么到此为止?

关联容器有一个find()方法也有同样的问题。序列容器有front()back(),同样有同样的问题。

这些缺失的显式const版本是省略的,还是故意的?

一个更广泛的API是有成本的,甚至只是在寻找你想要的函数时跳过它。

template<class T>
T const as_const(T&& t) noexcept(noexcept(T(std::declval<T>())) {
  return std::forward<T>(t);
}
template<class T>
T const& as_const(T& t) noexcept {
  return t;
}

做了大部分你想做的。它甚至会使cbegin过时。

(根据下面@ tc提供的n4380对上面代码所做的修改)。代码不同,因为我认为n4380在T&&的情况下略有错误。)

cbegin/cend的目的是解决一个特定的问题。考虑以下代码:

std::vector<int> & v = //... v is a non-const reference
// For clarity, I want this iterator to be a const_iterator.
// This works because iterator is implicitly convertible to const_iterator
std::vector<int>::const_iterator iter = find(v.begin(),v.end(),42);
// (1) Now I want to use iter in an algorithm
std::find(iter, v.end(), 38); //Error, can not deduce the template parameter for Iter. 
// (2) This works
std::find(iter, const_cast<const std::vector<int> &>(v).end(), 38);
// (3) This is the same as (2).
std::find(iter, v.cend(), 38);

问题是,由于模板推导规则的工作方式,编译器无法推导语句(1)中的模板迭代器参数,因为Container::iteratorContainer::const_iterator(可能)是两种完全不相关的类型(即使前者在后者中可以隐式转换)。

语句(2)不完全是一个漂亮的行,这就是为什么我们需要 cend()

现在,front(), back()等都返回一个引用。非const引用总是可以在模板化函数中推导为const,即:

template<class T> void f( const T & l, const T & r);
int main()
{
    int x; vector<int> v;
    //This will works even if the return type of front() is int&.
    f(x, v.front());
 }

因为标准要求Container::const_reference等于const Container::value_type &,所以cfront()/cback()不给我们买任何东西。


值得一提的是,其他容器库(看看你的Qt)是使用写时复制实现的。

这意味着在这样的容器上调用const函数可能比调用等效的非const版本要便宜得多,因为非const可能会复制整个容器的底层。

由于这个原因,Qt容器在其接口中有很多constFunction,用户可以自由选择正确的