以下の単純なプログラムを考えてみます。このプログラムは、セット内の要素への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参照を取得できるはずです。セッター関数を作成してループで使用すると、同じエラーが発生します。
セットは、値のみを持ち、キーのみを持つマップのようなものです。これらのキーは、セットの操作を高速化するツリーに使用されるため、変更できません。したがって、基礎となるツリーの制約が壊れないようにするには、すべての要素をconstにする必要があります。
std::set
は、含まれている値を使用して高速データ構造(通常は赤黒ツリー)を形成します。値を変更すると、構造全体を変更する必要があります。したがって、const
ness、std::set
を使用不可の状態にすることを防ぎます。
cpp参照 から:
セットでは、要素の値も要素を識別し(値自体がT型のキーです)、各値は一意である必要があります。セット内の要素の値は、コンテナ内で一度は変更できません(要素は常にconst)ですが、コンテナに対して挿入または削除できます。
動作は仕様です。
非constイテレータを指定すると、セット内の要素を変更するように促される可能性があります。その後の反復動作は未定義になります。
C++標準ではset<T>::iterator
はconst
なので、旧式のC++ 11以前の方法はまだ機能しません。