web-dev-qa-db-ja.com

std :: setがconst_iteratorの使用を強制しているように見えるのはなぜですか?

以下の単純なプログラムを考えてみます。このプログラムは、セット内の要素へのNON-const参照を使用してセットの値を反復処理しようとします。

#include <set>
#include <iostream>

class Int
{
public:
   Int(int value) : value_(value) {}
   int value() const { return value_; }
   bool operator<(const Int& other) const { return value_ < other.value(); }
private:
   int value_;
};

int
main(int argc, char** argv) {
   std::set<Int> ints;
   ints.insert(10);
   for (Int& i : ints) {
      std::cout << i.value() << std::endl;
   }
   return 0;
}

これをコンパイルすると、gccからエラーが発生します。

test.c: In function ‘int main(int, char**)’:
test.c:18:18: error: invalid initialization of reference of type ‘Int&’ from expression of type ‘const Int’  
for (Int& i : ints) {  
              ^  

はい、実際にはforループの要素を変更しようとしているわけではありません。しかし、要点は、セット自体がconst修飾されていないため、ループ内で使用する非const参照を取得できるはずです。セッター関数を作成してループで使用すると、同じエラーが発生します。

35
atomicpirate

セットは、値のみを持ち、キーのみを持つマップのようなものです。これらのキーは、セットの操作を高速化するツリーに使用されるため、変更できません。したがって、基礎となるツリーの制約が壊れないようにするには、すべての要素をconstにする必要があります。

45
nate

std::setは、含まれている値を使用して高速データ構造(通常は赤黒ツリー)を形成します。値を変更すると、構造全体を変更する必要があります。したがって、constness、std::setを使用不可の状態にすることを防ぎます。

11
lisyarus

cpp参照 から:

セットでは、要素の値も要素を識別し(値自体がT型のキーです)、各値は一意である必要があります。セット内の要素の値は、コンテナ内で一度は変更できません(要素は常にconst)ですが、コンテナに対して挿入または削除できます。

8
Ohad Eytan

動作は仕様です。

非constイテレータを指定すると、セット内の要素を変更するように促される可能性があります。その後の反復動作は未定義になります。

C++標準ではset<T>::iteratorconstなので、旧式のC++ 11以前の方法はまだ機能しません。