C++ cbegin/cend 背后的原因是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12001410/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 15:44:44  来源:igfitidea点击:

What is the reason behind cbegin/cend?

c++c++11iteratorconst-correctnessconst-iterator

提问by Andrey

I wonder why cbeginand cendwere introduced in C++11?

我想知道为什么cbegin并且cend是在 C++11 中引入的?

What are cases when calling these methods makes a difference from const overloads of beginand end?

什么情况下使用这些方法时,使得从常量重载的差异beginend

回答by Nicol Bolas

It's quite simple. Say I have a vector:

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

std::vector<int> vec;

I fill it with some data. Then I want to get some iterators to it. Maybe pass them around. Maybe to std::for_each:

我用一些数据填充它。然后我想得到一些迭代器。也许把它们传递出去。也许std::for_each

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

In C++03, SomeFunctorwas free to be able to modifythe parameter it gets. Sure, SomeFunctorcould take its parameter by value or by const&, but there's no way to ensurethat it does. Not without doing something silly like this:

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

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

Now, we introduce cbegin/cend:

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

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

Now, we have syntactic assurances that SomeFunctorcannot modify the elements of the vector (without a const-cast, of course). We explicitly get const_iterators, and therefore SomeFunctor::operator()will be called with const int &. If it takes it's parameters as int &, C++ will issue a compiler error.

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



C++17 has a more elegant solution to this problem: std::as_const. Well, at least it's elegant when using range-based for:

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

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

This simply returns a const&to the object it is provided.

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

回答by Stefan Majewsky

Beyond what Nicol Bolas said in his answer, consider the new autokeyword:

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

auto iterator = container.begin();

With auto, there's no way to make sure that begin()returns a constant operator for a non-constant container reference. So now you do:

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

auto const_iterator = container.cbegin();

回答by Johannes Schaub - litb

Take this as a practical usecase

将此作为实际用例

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

The assignment fails because itis a nonconst iterator. If you used cbegin initially, the iterator would have had the right type.

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

回答by TemplateRex

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

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

so that a programmer can directly obtain a const_iterator from even a non-const container

以便程序员可以直接从非常量容器中获取 const_iterator

They gave this example

他们举了这个例子

vector<MyType> v;

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

However, when a container traversal is intended for inspection only, it is a generally preferred practice to use a const_iterator in order to permit the compiler to diagnose const-correctness violations

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

Note that the working paper also mentions adapter templates, that now have been finalized as std::begin()and std::end()and that also work with native arrays. The corresponding std::cbegin()and std::cend()are curiously missing as of this time, but they might also be added.

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

回答by chris g.

Just stumbled upon this question... I know it's alredy answerd and it's just a side node...

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

auto const it = container.begin()is a different type then auto it = container.cbegin()

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

the difference for int[5](using pointer, which i know don't have the begin method but show nicely the difference... but would work in c++14 for std::cbegin()and std::cend(), which is essentially what one should use when it's here)...

的区别int[5](使用指针,我知道它没有 begin 方法,但很好地显示了区别......但可以在 c++14 for 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

回答by hkBattousai

iteratorand const_iteratorhave inheritance relationship and an implicit conversion occurs when compared with or assigned to the other type.

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)
{
    // ...
}

Using cbegin()and cend()will increase performance in this case.

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

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

回答by PAVAN KUMAR

its simple, cbegin returns a constant iterator where begin returns just an iterator

它很简单,cbegin 返回一个常量迭代器,而 begin 只返回一个迭代器

for better understanding lets take two scenarios here

为了更好地理解,让我们在这里假设两个场景

scenario - 1 :

场景 - 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;
}

this will run because here iterator i is not constant and can be incremented by 5

这将运行,因为这里迭代器 i 不是常数,可以增加 5

now let's use cbegin and cend denoting them as constant iterators scenario - 2 :

现在让我们使用 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;
}

this is not going to work, because you cant update the value using cbegin and cend which returns the constant iterator

这是行不通的,因为您无法使用返回常量迭代器的 cbegin 和 cend 更新值