我想知道为什么在c++ 11中引入cbegin和cend ?

在什么情况下,调用这些方法与const重载begin和end有区别?


当前回答

将此作为一个实际用例

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

赋值失败是因为它是非const迭代器。如果一开始使用cbegin,迭代器的类型就正确了。

其他回答

除了Nicol Bolas在他的回答中所说的,考虑一下新的auto关键字:

auto iterator = container.begin();

使用auto,无法确保begin()为非常量容器引用返回常量操作符。所以现在你知道了:

auto const_iterator = container.cbegin();

Iterator和const_iterator具有继承关系,当与另一类型比较或赋值给另一类型时,会发生隐式转换。

class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
    // ...
}

在这种情况下,使用cbegin()和cend()将提高性能。

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}

它的简单、 Cbegin返回一个常量迭代器,而begin只返回一个迭代器

为了更好地理解,让我们举两个例子

场景- 1:

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.begin();i< v.end();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

这将运行,因为这里的迭代器I不是常量,可以加5

现在让我们使用cbegin和cend将它们表示为常量迭代器 场景- 2:

#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;

for (int i = 1; i < 6; ++i)
{
    /* code */
    v.push_back(i);
}

for(auto i = v.cbegin();i< v.cend();i++){
    *i = *i + 5;
}

for (auto i = v.begin();i < v.end();i++){
    cout<<*i<<" ";
}

return 0;
}

这是行不通的,因为你不能使用cbegin和cend来更新值,这将返回常量迭代器

将此作为一个实际用例

void SomeClass::f(const vector<int>& a) {
  auto it = someNonConstMemberVector.begin();
  ...
  it = a.begin();
  ...
}

赋值失败是因为它是非const迭代器。如果一开始使用cbegin,迭代器的类型就正确了。

从http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:

这样程序员就可以直接从 non-const容器

他们举了这个例子

vector<MyType> v;

// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
    // use *it ...
}

然而,当容器遍历仅用于检查时, 通常首选的做法是按顺序使用const_iterator 允许编译器诊断违反常量正确性的情况

请注意,工作论文还提到了适配器模板,现在已最终确定为std::begin()和std::end(),它们也适用于本机数组。奇怪的是,相应的std::cbegin()和std::cend()到目前为止都没有,但它们也可能被添加。