210

我想知道为什么cbegincend在 C++11 中引入?

begin在哪些情况下调用这些方法与和的 const 重载有所不同end

4

7 回答 7

244

这很简单。假设我有一个向量:

std::vector<int> vec;

我用一些数据填充它。然后我想得到一些迭代器。也许让他们四处走动。也许到std::for_each

std::for_each(vec.begin(), vec.end(), SomeFunctor());

在 C++03 中,SomeFunctor可以自由地修改它获取的参数。当然,SomeFunctor可以通过 value 或 by 获取其参数const&,但无法确保它确实如此。并非没有做这样愚蠢的事情:

const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());

现在,我们介​​绍cbegin/cend

std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());

SomeFunctor现在,我们有了不能修改向量元素的语法保证(当然,没有 const-cast)。我们明确地得到const_iterators,因此SomeFunctor::operator()将被调用const int &。如果它的参数为int &,C++ 将发出编译器错误。


C++17 对这个问题有一个更优雅的解决方案:std::as_const. 好吧,至少在使用 range-based 时它很优雅for

for(auto &item : std::as_const(vec))

这只是将 a 返回const&到它提供的对象。

于 2012-08-17T07:24:36.390 回答
69

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

auto iterator = container.begin();

使用auto,无法确保begin()为非常量容器引用返回常量运算符。所以现在你做:

auto const_iterator = container.cbegin();
于 2012-08-17T07:27:09.240 回答
15

将此作为实际用例

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

赋值失败,因为it它是一个非常量迭代器。如果您最初使用 cbegin,则迭代器将具有正确的类型。

于 2012-08-17T07:34:35.130 回答
8

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

这样程序员甚至可以直接从非常量容器中获取 const_iterator

他们举了这个例子

vector<MyType> v;

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

但是,当容器遍历仅用于检查时,通常首选的做法是使用 const_iterator 以允许编译器诊断 const 正确性违规

请注意,工作文件还提到了适配器模板,这些模板现在已经最终确定为std::begin()andstd::end()并且也适用于本机数组。到目前为止,相应的std::cbegin()std::cend()奇怪地丢失了,但它们也可能会被添加。

于 2012-08-17T07:24:47.457 回答
6

只是偶然发现了这个问题......我知道它已经得到了回答,它只是一个侧面节点......

auto const it = container.begin()那么是不同的类型auto it = container.cbegin()

的区别int[5](使用指针,我知道它没有 begin 方法,但很好地显示了区别......但可以在 c++14 中为std::cbegin()and工作std::cend(),这基本上是当它在这里时应该使用的)......

int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers);      // type is int const* -> value is const
于 2014-06-05T20:34:34.160 回答
2

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()andcend()将提高性能。

for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
    // ...
}
于 2014-12-29T11:30:12.890 回答
0

它很简单,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 更新值,它返回常量迭代器

于 2020-04-09T18:21:48.493 回答