所以,为了允许代码如
auto vect = ...;
auto it = vect.begin(), end = vect.end(); // want const_iterator, getting iterator
选择begin()
和end()
的正确重载,即使对于非const容器,也会添加更明确的cbegin()
/ cend()
函数。
为什么要停在那里?
关联容器的find()
方法具有相同的问题。序列容器有front()
和back()
,同样存在同样的问题。
这些遗漏的显式const版本是遗漏还是设计?
答案 0 :(得分:7)
更广泛的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
过时。
(根据以下@ T.C提供的n4380对上述代码进行了修改。代码不同,因为我认为n4380在T&&
情况下略有错误。)
答案 1 :(得分:3)
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::iterator
和Container::const_iterator
(可能)完全是两个不相关的类型(即使前者在后者中可以隐式转换)。
声明(2)并不完美,这就是我们需要 cend()
的原因。
现在,front()
,back()
et similia都会返回引用。非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)是使用Copy-On-Write实现的。
这意味着在这样的容器上调用const函数可能比调用等效的非const版本便宜得多,因为非const可能会复制整个容器。
因此,Qt容器的界面中有很多constFunction
,用户可以自由选择正确的。{/ p>