Answers:
这很简单。假设我有一个向量:
std::vector<int> vec;
我用一些数据填充它。然后,我想获得一些迭代器。也许把它们传过去。也许去std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
在C ++ 03中,SomeFunctor
可以随意修改它获取的参数。当然,SomeFunctor
可以按值或按接受其参数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_iterator
s,因此SomeFunctor::operator()
将被调用const int &
。如果将其参数设为int &
,则C ++将发出编译器错误。
C ++ 17对这个问题有一个更优雅的解决方案:std::as_const
。好吧,至少在使用基于范围的方法时很优雅for
:
for(auto &item : std::as_const(vec))
这只是将a返回const&
给它提供的对象。
std::cbegin/cend
免费函数std::begin/std::end
存在。这是委员会的监督。如果确实存在这些功能,那通常就是使用它们的方式。
std::cbegin/cend
将在C ++ 14中添加。参见en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
相当于for(const auto &item : vec)
什么?
const
引用放在该项目上不会对其进行修改。Nicol将容器视为const,因此auto
推论出一个const
引用。IMO auto const& item
更加容易和清晰。目前尚不清楚为什么std::as_const()
这里很好。我可以看到,当将一些非非const
通用代码传递给我们无法控制要使用的类型的通用代码时,这很有用,但是有了range- for
,我们可以了,所以这似乎给我增加了噪音。
除了Nicol Bolas在回答中所说的以外,请考虑以下新auto
关键字:
auto iterator = container.begin();
使用auto
,无法确保begin()
为非常数容器引用返回常数运算符。所以现在您要做:
auto const_iterator = container.cbegin();
const_iterator
只是另一个标识符。这两个版本均未使用通常的成员typedefs decltype(container)::iterator
或查找decltype(container)::const_iterator
。
const_iterator
with auto
:编写一个称为make_const
对对象参数进行限定的辅助函数模板。
将此作为实际用例
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
分配失败,因为它it
是一个非常量迭代器。如果最初使用cbegin,则迭代器将具有正确的类型。
从http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf:
这样程序员甚至可以从非const容器中直接获取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()
和形式完成,并且std::end()
还可以用于本机阵列。到目前为止,相应的std::cbegin()
和std::cend()
奇怪地丢失了,但是也可以添加它们。
只是偶然发现了这个问题...我知道它已经被回答了,而且只是一个副节点...
auto const it = container.begin()
然后是另一种类型 auto it = container.cbegin()
的区别int[5]
(使用指针,我知道没有begin方法,但是很好地显示了区别...但是可以在c ++ 14中用于std::cbegin()
和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
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)
{
// ...
}
const
(即,它不是语义上正确和安全的代码),它的主要好处是性能。但是,尽管您有观点,但(A)auto
认为这不是问题;(B)在谈论性能时,您错过了这里应该做的主要事情:end
在for
循环的init-condition中声明它的一个副本来缓存迭代器,并与之进行比较,而不是通过每次迭代的价值。那会让你的观点更好。:P
const
可以(几乎间接)带来性能收益。以防万一有人读到这篇文章可能会认为“ const
如果生成的代码从未受到任何影响,我不会费心添加”,这是不正确的。
简单来说,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更新值,该值将返回常量迭代器