[解決済み】C++11のrange-based forの正しい使い方は?
質問
C++11の範囲ベースの
for
?
どのような構文にすればよいのでしょうか?
for (auto elem : container)
,
または
for (auto& elem : container)
または
for (const auto& elem : container)
?
それとも他の?
解決方法は?
TL;DR: 以下のガイドラインを考慮してください。
-
について 観察 要素には、次の構文を使用します。
for (const auto& elem : container) // capture by const reference
-
もし、オブジェクトが コピー代が安い (例えば
int
s,double
sなど)。 というように、少し簡略化した形で使用することが可能です。for (auto elem : container) // capture by value
-
-
について モディファイ を使うと、その場にある要素を使うことができます。
for (auto& elem : container) // capture by (non-const) reference
-
コンテナが "プロキシイテレータ" (例えば
std::vector<bool>
) を使ってください。for (auto&& elem : container) // capture by &&
-
もちろん、必要な場合は
ローカルコピー
をキャプチャすることで、ループ本体内の要素の
値で
(
for (auto elem : container)
)を選択するとよいでしょう。
詳細なディスカッション
の区別を始めましょう。 観察する コンテナ内の要素 vs. 修正 をそのまま使用します。
要素の観察
簡単な例で考えてみましょう。
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v)
cout << x << ' ';
上記のコードでは、要素 (
int
の中にある
vector
:
1 3 5 7 9
ここで、ベクトルの要素が単なる整数ではない、別のケースを考えてみましょう。 しかし、カスタムコピーコンストラクタなどを持つ、より複雑なクラスのインスタンスです。
// A sample test class, with custom copy semantics.
class X
{
public:
X()
: m_data(0)
{}
X(int data)
: m_data(data)
{}
~X()
{}
X(const X& other)
: m_data(other.m_data)
{ cout << "X copy ctor.\n"; }
X& operator=(const X& other)
{
m_data = other.m_data;
cout << "X copy assign.\n";
return *this;
}
int Get() const
{
return m_data;
}
private:
int m_data;
};
ostream& operator<<(ostream& os, const X& x)
{
os << x.Get();
return os;
}
もし、上記の
for (auto x : v) {...}
の構文を、この新しいクラスで使用することができます。
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (auto x : v)
{
cout << x << ' ';
}
のような出力になります。
[... copy constructor calls for vector<X> initialization ...] Elements: X copy ctor. 1 X copy ctor. 3 X copy ctor. 5 X copy ctor. 7 X copy ctor. 9
出力から読み取れるように
コピーコンストラクタ
の呼び出しは、範囲ベースのforループの反復中に行われます。
これは、私たちが
キャプチャー
コンテナからの要素
値で
(その
auto x
の部分は
for (auto x : v)
).
これは
非効率的
のインスタンスである場合、例えば、これらの要素が
std::string
,
ヒープメモリの割り当てを行うことができ、メモリマネージャへの負荷が高くなります。
これは,単に
見る
コンテナ内の要素
そこで、より良い構文が用意されています: capture
によって
const
参照
は、すなわち
const auto&
:
vector<X> v = {1, 3, 5, 7, 9};
cout << "\nElements:\n";
for (const auto& x : v)
{
cout << x << ' ';
}
これで出力は
[... copy constructor calls for vector<X> initialization ...] Elements: 1 3 5 7 9
偽の(そして高価になる可能性のある)コピーコンストラクタの呼び出しがないこと。
では、いつ
を観察する
要素をコンテナ内で使用する場合(つまり,読み取り専用でアクセスする場合)。
の場合、次のような構文で問題ありません。
チープトゥコピー
のようなタイプは
int
,
double
など。
for (auto elem : container)
でキャプチャします。
const
の参照は、より良い
一般的な場合
,
無駄な(そして高価になりかねない)コピーコンストラクタの呼び出しを避けるためです。
for (const auto& elem : container)
コンテナ内の要素を変更する
もし私たちが
モディファイ
を使用してコンテナ内の要素に範囲ベースの
for
,
上記
for (auto elem : container)
と
for (const auto& elem : container)
の構文がおかしい。
実は、前者の場合
elem
を格納します。
コピー
元の
そのため、その要素に加えられた変更は失われるだけで、永続的に保存されるわけではありません。
をコンテナに入れるなど。
vector<int> v = {1, 3, 5, 7, 9};
for (auto x : v) // <-- capture by value (copy)
x *= 10; // <-- a local temporary copy ("x") is modified,
// *not* the original vector element.
for (auto x : v)
cout << x << ' ';
出力は初期シーケンスだけです。
1 3 5 7 9
を使うという試みがなされています。
for (const auto& x : v)
は、コンパイルに失敗するだけです。
g++は次のようなエラーメッセージを出力します。
TestRangeFor.cpp:138:11: error: assignment of read-only reference 'x' x *= 10; ^
この場合の正しいアプローチは、nonでキャプチャすることです。
const
を参照してください。
vector<int> v = {1, 3, 5, 7, 9};
for (auto& x : v)
x *= 10;
for (auto x : v)
cout << x << ' ';
出力は(予想通り)です。
10 30 50 70 90
これは
for (auto& elem : container)
の構文は、より複雑な型に対しても有効です。
を考えるなど。
vector<string>
:
vector<string> v = {"Bob", "Jeff", "Connie"};
// Modify elements in place: use "auto &"
for (auto& x : v)
x = "Hi " + x + "!";
// Output elements (*observing* --> use "const auto&")
for (const auto& x : v)
cout << x << ' ';
が出力されます。
Hi Bob! Hi Jeff! Hi Connie!
プロキシイテレータの特殊なケース
があるとします。
vector<bool>
で、論理的なブーリアン状態を反転させたいとします。
という構文で、その要素について説明します。
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
上記のコードは、コンパイルに失敗します。
g++はこのようなエラーメッセージを出力します。
TestRangeFor.cpp:168:20: error: invalid initialization of non-const reference of type 'std::_Bit_reference&' from an rvalue of type 'std::_Bit_iterator::referen ce {aka std::_Bit_reference}' for (auto& x : v) ^
問題なのは
std::vector
テンプレートは
専門的
に対して
bool
である。
という実装があります。
パック
その
bool
を使用してスペースを最適化します(各ブーリアン値は
1ビットに格納され、1バイトに8ビットのboolean"boolean;が格納されます)。
そのため(1ビットへの参照を返すことができないため)。
vector<bool>
は、いわゆる
プロキシイテレータです。
というパターンがあります。
プロキシイテレータとは、参照されたときに
ない
は
通常の
bool &
を返しますが、その代わりに(値で
一時的なオブジェクト
,
というもので、これは
プロキシクラス
に変換することができます。
bool
.
(参照
この質問と関連する回答
StackOverflowのこちら)
の要素をその場で変更するには
vector<bool>
を使用する)、新しい種類の構文
auto&&
)
を使用しなければならない。
for (auto&& x : v)
x = !x;
以下のコードでは正常に動作します。
vector<bool> v = {true, false, false, true};
// Invert boolean status
for (auto&& x : v) // <-- note use of "auto&&" for proxy iterators
x = !x;
// Print new element values
cout << boolalpha;
for (const auto& x : v)
cout << x << ' ';
と出力します。
false true true false
なお、この
for (auto&& elem : container)
の構文は、他のケースでも有効です。
通常の(プロキシでない)イテレータの場合(たとえば
vector<int>
または
vector<string>
).
(余談ですが、前述の "observing" の構文で
for (const auto& elem : container)
はプロキシイテレータの場合にも問題なく動作します)。
概要
以上の議論をまとめると、次のような指針になります。
-
について 観察 要素には、次の構文を使用します。
for (const auto& elem : container) // capture by const reference
-
もし、オブジェクトが コピー代が安い (例えば
int
s,double
sなど)。 というように、少し簡略化した形で使用することが可能です。for (auto elem : container) // capture by value
-
-
について モディファイ を使うと、その場にある要素を使うことができます。
for (auto& elem : container) // capture by (non-const) reference
-
コンテナが "プロキシイテレータ" (例えば
std::vector<bool>
) を使ってください。for (auto&& elem : container) // capture by &&
-
もちろん、必要な場合は
ローカルコピー
をキャプチャすることで、ループ本体内の要素の
値で
(
for (auto elem : container)
)を選択するとよいでしょう。
ジェネリックコードに関する補足説明
で
ジェネリックコード
について仮定することができないので、一般的な型である
T
で、コピーが安い。
観察
モードでは、常に
for (const auto& elem : container)
.
(これは、高価な無駄なコピーを引き起こす可能性はなく、以下のような安価なコピータイプでも問題なく動作します。
int
のようなプロキシイテレータを使用するコンテナにも適用できます。
std::vector<bool>
.)
さらに
修正
モードでは、もし
汎用コード
をプロキシイテレータの場合にも使えるようにするための最良の選択肢は
for (auto&& elem : container)
.
(のような、通常の非プロキシ型イテレータを使用するコンテナでも問題なく動作します)。
std::vector<int>
または
std::vector<string>
.)
そこで 汎用コード は、次のような指針を示すことができる。
-
について 観察 要素を使用します。
for (const auto& elem : container)
-
について モディファイ を使うと、その場にある要素を使うことができます。
for (auto&& elem : container)
関連
-
[解決済み】C++ クラスヘッダが含まれているときに「不明な型」があるのはなぜですか?重複
-
[解決済み】getline()が何らかの入力の後に使用されると動作しない 【重複あり
-
[解決済み】'std::cout'への未定義の参照
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み] Javaの「for each」ループはどのように機能するのですか?
-
[解決済み] C++11の'typedef'と'using'の違いは何ですか?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】getline()が何らかの入力の後に使用されると動作しない 【重複あり
-
[解決済み】クラステンプレートの引数リストがない
-
[解決済み】IntelliSense:オブジェクトに、メンバー関数と互換性のない型修飾子がある
-
[解決済み】'cout'は型名ではない
-
[解決済み】C++の変数はイニシャライザーを持っているが、不完全な型?
-
[解決済み】C++の余分な資格エラー
-
[解決済み】1つ以上の多重定義されたシンボルが見つかる
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み】Eclipse IDEでC++エラー「nullptrはこのスコープで宣言されていません」が発生する件