web-dev-qa-db-ja.com

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

なぜcbegincendがC++ 11で導入されたのだろうか?

これらのメソッドを呼び出すと、beginおよびendのc​​onstオーバーロードとは異なる場合はどうなりますか?

179
Andrey

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

std::vector<int> vec;

いくつかのデータを入力します。次に、イテレータを取得します。たぶんそれらを渡します。多分std::for_eachに:

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

C++ 03では、SomeFunctorは、取得したパラメーターをmodifyできるようになりました。確かに、SomeFunctorは値またはconst&でパラメーターを取ることができますが、それをensureする方法はありません。このような愚かなことをせずに:

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キャストなしで)。 const_iteratorsを明示的に取得するため、SomeFunctor::operator()const int &で呼び出されます。 int &としてパラメータを取る場合、C++はコンパイラエラーを発行します。


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

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

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

214
Nicol Bolas

Nicol Bolasが 彼の答え で言ったことに加えて、新しいautoキーワードを検討してください:

auto iterator = container.begin();

autoでは、begin()が非定数コンテナ参照に対して定数演算子を返すようにする方法はありません。だから今あなたは:

auto const_iterator = container.cbegin();
63
Stefan Majewsky

これを実用的なユースケースとして活用してください

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-correctness違反を診断できるようにするために、const_iteratorを使用することをお勧めします。

ワーキングペーパーではアダプターテンプレートについても言及していることに注意してください。アダプターテンプレートはstd::begin()およびstd::end()として確定されており、ネイティブ配列でも機能します。現時点では、対応するstd::cbegin()std::cend()は不思議なことに欠落していますが、追加される可能性もあります。

8
TemplateRex

この質問につまずいた...私はそれがすでに答えられていることを知っています、それは単なるサイドノードです...

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
5
chris g.

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