[解決済み] C++11のforループの落とし穴はこれか?
質問
3つのdoubleを保持する構造体があり、いくつかのメンバ関数があるとします。
struct Vector {
double x, y, z;
// ...
Vector &negate() {
x = -x; y = -y; z = -z;
return *this;
}
Vector &normalize() {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
// ...
};
これは単純化するために少し工夫したものですが、似たようなコードが世の中にあることに同意していただけると思います。メソッドによって、例えば便利に連鎖させることができます。
Vector v = ...;
v.normalize().negate();
あるいは、さらに
Vector v = Vector{1., 2., 3.}.normalize().negate();
もしbegin()とend()関数があれば、新しいスタイルのforループでVectorを使い、例えば3つの座標x、y、zをループすることができます(Vectorを例えばStringに置き換えれば、よりquot; useful"な例を構築できることは間違いないでしょう)。
Vector v = ...;
for (double x : v) { ... }
もできる。
Vector v = ...;
for (double x : v.normalize().negate()) { ... }
というように、また
for (double x : Vector{1., 2., 3.}) { ... }
しかし、以下は壊れている(ように見える)。
for (double x : Vector{1., 2., 3.}.normalize()) { ... }
前の2つの使い方の論理的な組み合わせのように見えますが、前の2つは全く問題ないのに対して、この最後の使い方はぶら下がった参照を作り出していると思います。
- これは正しく、広く評価されていますか。
- 上記のうち、避けるべき「悪い」部分はどこですか?
- 範囲ベースのforループの定義を変更し、for式で構築されたテンポラリーがループの間存在するようにすれば、言語は改善されますか。
どのように解決するのですか?
<ブロッククオートこれは正しく、広く評価されているのでしょうか?
はい、あなたの理解は正しいです。
<ブロッククオート上記のうち、避けるべき"bad"の部分はどこでしょうか?
悪い部分は、関数から返された一時的なものへのl値の参照を取り、それをr値の参照にバインドすることです。これと同じように悪いことです。
auto &&t = Vector{1., 2., 3.}.normalize();
一時的な
Vector{1., 2., 3.}
からの返り値をコンパイラが知らないため、その寿命を延ばすことができません。
normalize
の戻り値がそれを参照していることをコンパイラは知らないからです。
for式の中で構築された一時的なものがループの期間中存在するように、範囲ベースのforループの定義を変更することによって、言語は改善されますか?
それはC++がどのように動作するかと非常に矛盾しています。
テンポラリーで連鎖した式や、式のための様々な遅延評価方法を使用する人々が作る特定の混乱を防ぐことができるでしょうか?はい、そうです。しかし、それはまた、特殊なケースのコンパイラーコードを必要とし、また、なぜそれが その他 式構成要素で動作しない理由を混乱させることになります。
より合理的な解決策は、関数の返り値が常に、その関数への参照であることをコンパイラに知らせる方法でしょう。
this
への参照であり、したがって、戻り値が一時的に拡張する構成にバインドされている場合、それは正しい一時的なものを拡張します。これは言語レベルの解決策ですが。
現時点では(コンパイラがサポートしていれば)、以下のようにすることができます。
normalize
はできません。
はテンポラリーで呼び出されます。
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector &normalize() && = delete;
};
これにより
Vector{1., 2., 3.}.normalize()
はコンパイルエラーになりますが
v.normalize()
は正常に動作します。もちろん、このような正しいことはできません。
Vector t = Vector{1., 2., 3.}.normalize();
しかし、正しくないこともできなくなります。
あるいは、コメントで提案されているように、rvalue参照版が参照ではなく、値を返すようにすることもできます。
struct Vector {
double x, y, z;
// ...
Vector &normalize() & {
double s = 1./sqrt(x*x+y*y+z*z);
x *= s; y *= s; z *= s;
return *this;
}
Vector normalize() && {
Vector ret = *this;
ret.normalize();
return ret;
}
};
もし
Vector
が実際に移動するリソースを持つ型であった場合は
Vector ret = std::move(*this);
を使うことができます。名前付き戻り値の最適化により、これはパフォーマンスの点で合理的に最適化されています。
関連
-
[解決済み】 != と =! の違いと例(C++の場合)
-
[解決済み】ファイルから整数を読み込んで配列に格納する C++ 【クローズド
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] 配列の反復処理に "for...in "を使用するのは、なぜ良くないのでしょうか?
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み] Javaの「for each」ループはどのように機能するのですか?
-
[解決済み] なぜpythonはforやwhileループの後に'else'を使うのですか?
-
[解決済み] JavaScriptのFor.Inループ - キーと値のペア
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
-
[解決済み】カスタムタイプを「範囲ベースのforループ」で動作させる方法とは?
最新
-
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++でint型に無限大を設定する
-
[解決済み】LLVMで暗黙のうちに削除されたコピーコンストラクタの呼び出し
-
[解決済み】IntelliSense:オブジェクトに、メンバー関数と互換性のない型修飾子がある
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] 配列のベクトルを扱う正しい方法
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?