cbegin / cendの背後にある理由は何ですか?


回答:


226

とても簡単です。ベクトルがあるとしましょう:

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_iterators を取得するため、SomeFunctor::operator()で呼び出されconst int &ます。パラメータとしてを使用するとint &、C ++はコンパイラエラーを発行します。


C ++ 17には、この問題に対するよりエレガントなソリューションがありますstd::as_const。まあ、少なくとも範囲ベースを使用する場合はエレガントですfor

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

これは、const&提供されたオブジェクトにa を返すだけです。


1
新しいプロトコルはvec.cbegin()ではなくcbegin(vec)だと思いました。
Kaz Dragon

20
@カズ:存在std::cbegin/cendするような無料の機能はありませんstd::begin/std::end。委員会の監督でした。これらの関数が存在する場合、それは一般的にそれらを使用する方法です。
Nicol Bolas

20
どうやら、std::cbegin/cendC ++ 14で追加されます。en.cppreference.com/w/cpp/iterator/beginを
Adi Shavit

8
@NicolBolasは?とfor(auto &item : std::as_const(vec))同等for(const auto &item : vec)です。
luizfls 2017年

8
@luizflsはい。あなたのコードは、アイテムをconst参照に置くことによって変更されないことを示しています。Nicolはコンテナーをconstと見なしているためautoconst参照を推定します。IMO auto const& itemはより簡単で明確です。なぜstd::as_const()ここが良いのかは不明です。const使用されるタイプを制御できないが、範囲を持つ汎用コードに何かを渡すときに、それが役立つことがわかりますfor追加できるノイズのように見えます。
underscore_d

66

ニコルボーラスが彼の回答で述べたことを超えて、新しいautoキーワードを検討してください:

auto iterator = container.begin();

ではautobegin()非定数コンテナ参照に対して定数演算子が返されることを確認する方法はありません。だから今あなたはします:

auto const_iterator = container.cbegin();

2
@allyourcode:役に立たない。コンパイラにとって、これconst_iteratorは単なる別の識別子です。どちらのバージョンも、通常のメンバーtypedef decltype(container)::iteratorまたはのルックアップを使用しませんdecltype(container)::const_iterator
アシェプラー2013年

2
@aschepler 2番目の文は理解できませんが、質問の「auto」の前にある「const」を逃したと思います。どんな自動車でも、const_iteratorはconstでなければならないようです。
allyourcode 2013年

26
@allyourcode:それはあなたに定数であるイテレータを与えるでしょう、しかしそれはイテレータと定数データとは非常に異なります。
aschepler 2013年

2
確実にconst_iteratorwith を取得する簡単な方法があります。オブジェクト引数を修飾するためにauto呼び出される補助関数テンプレートを記述しますmake_const
コロンボ2015年

17
たぶん私はもはやC ++の考え方ではないかもしれませんが、「単純な方法」と「補助関数テンプレートを書く」の概念の間の関係を理解できません。;)
Stefan Majewsky

15

これを実用的な使用例として考えてください

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

itは非constイテレータなので、割り当ては失敗します。最初にcbeginを使用した場合、イテレータは正しい型でした。


8

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()が不思議なことに欠落していますが、追加される可能性もあります。


5

この質問に出くわしただけです...私はそれがすでに答えられていて、それが単なるサイドノードであることを知っています...

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

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()およびcend()を使用するとパフォーマンスが向上します。

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

イテレータを初期化して比較するときに変換を回避することでパフォーマンスが節約されることを意味するのにしばらく時間がかかりました。const主な利点はパフォーマンスであるという一般的な神話ではありません(これは意味がありません:意味的に正しく安全なコードです)。しかし、あなたがポイントを持っている間、(A)autoそれは問題ではありません。(B)パフォーマンスについて話しているときに、ここでやるべき主要なことを見逃しましendた。forループのinit-conditionでイテレータのコピーを宣言してキャッシュし、新しいコピーを取得する代わりにそれと比較します。すべての反復の値。それはあなたのポイントをより良くするでしょう。:P
underscore_d

@underscore_d constは、constキーワード自体に魔法があるためではなく、データが変更されないことがわかっている場合にコンパイラーがいくつかの最適化を有効にできるため、パフォーマンスの向上に役立ちます。この実例については、Jason Turnerの講演からこのビットを確認してください。
ブレインプロット

@brainplotそれができないとは言わなかった。それは主な利点ではなく、意味のある正しい安全なコードが本当の利点である場合、それは誇張されていると私は言いました。
underscore_d

@underscore_dはい、私はそれに同意します。私はそれを明示しているだけでしたconst(ほとんど間接的に)パフォーマンスの向上につながる可能性がある。これを読んでいる人が「const生成されたコードがまったく影響を受けないのであれば、追加する手間を省く」と考えるかもしれませんが、これは真実ではありません。
ブレインプロット

0

その単純な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を使用して値を更新できないため、これは機能しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.