回答:
とても簡単です。ベクトルがあるとしましょう:
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))
これは、const&
提供されたオブジェクトにa を返すだけです。
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
使用されるタイプを制御できないが、範囲を持つ汎用コードに何かを渡すときに、それが役立つことがわかりますfor
追加できるノイズのように見えます。
ニコルボーラスが彼の回答で述べたことを超えて、新しいauto
キーワードを検討してください:
auto iterator = container.begin();
ではauto
、begin()
非定数コンテナ参照に対して定数演算子が返されることを確認する方法はありません。だから今あなたはします:
auto const_iterator = container.cbegin();
const_iterator
は単なる別の識別子です。どちらのバージョンも、通常のメンバーtypedef 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
は非constイテレータなので、割り当ては失敗します。最初に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-correctness違反を診断できるようにするために、const_iteratorを使用することが一般的に推奨される方法です。
ワーキングペーパーは、アダプターテンプレートについても言及していることに注意してください。アダプターテンプレートは、として確定され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はイテレータのみを返します
理解を深めるために、ここで2つのシナリオを考えてみましょう
シナリオ-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を使用して値を更新できないため、これは機能しません。