Herb Sutter氏のCppCon 2014の講演(Back to Basics: Modern C++ Style)で、値を取るセッター・メンバー関数が推奨されていないのはなぜか?
疑問点
Herb Sutter の CppCon 2014 の講演で、Back to Basics: モダン C++ スタイルについて、彼はスライド 28 で言及しています ( スライドのウェブコピーはこちら )でこのパターンに言及しています。
class employee {
std::string name_;
public:
void set_name(std::string name) noexcept { name_ = std::move(name); }
};
彼は、テンポラリで set_name() を呼び出すと noexcept-ness が強くならないため、これが問題になると言っています (彼は "noexcept-ish" というフレーズを使用しています)。
さて、私は最近の自分の C++ コードで上記のパターンをかなり多用しています。これは主に、毎回 set_name() のコピーを 2 つ入力する必要がなくなるからです。しかし、Herbのフレーズ"このnoexceptは 問題あり std::string の移動代入演算子は noexcept であり、そのデストラクタも noexcept なので、上の set_name() は noexcept が保証されているように見えます。しかし、コンパイラが例外を投げる可能性があります。 の前に set_name() がパラメータを準備するときに例外を投げる可能性があることがわかりますが、私はそれが問題であると見なすのに苦労しています。
後のスライド32で、ハーブは上記がアンチパターンであることを明確に述べています。私が怠惰になることで悪いコードを書いてしまわないように、誰かその理由を説明してくれませんか?
どのように解決するのか?
他の人がカバーした
noexcept
の推論は上記の通りです。
Herbは効率性の側面について、講演の中でより多くの時間を費やしました。問題は、割り当てではなく、不必要な再割り当てにあります。あなたが1つの
std::string
を別の文字列にコピーするとき、コピーされるデータを保持するのに十分なスペースがあれば、コピールーチンはコピー先の文字列の割り当てられたストレージを再利用します。移動の割り当てを行う場合、移動先の文字列の既存のストレージは、元の文字列からストレージを引き継ぐので、割り当てを解除する必要があります。コピー&ムーブのイディオムでは、テンポラリを渡さなかった場合でも、常に割り当て解除を行わなければなりません。これが、この講演の後半で示される、恐ろしいパフォーマンスの原因です。彼のアドバイスは、代わりに const ref を受け取り、もしそれが必要だと判断したら、r-value 参照のオーバーロードを持つことでした。これにより、非一時的なものについては既存のストレージにコピーして割り当て解除を回避し、一時的なものについては移動して割り当て解除の代金を支払うという、両方の利点が得られます (移動前に移動先が割り当て解除を行うか、コピー後に移動元が割り当て解除を行うかです)。
メンバ変数に割り当てを解除するストレージがないため、上記はコンストラクタには適用されません。コンストラクタはしばしば複数の引数を取り、各引数の const ref/r-value ref オーバーロードを行う必要がある場合、コンストラクタのオーバーロードが組み合わせ的に爆発してしまうので、これは良いことです。
ここで問題になるのは、コピー時に std::string のようなストレージを再利用するクラスがどれだけあるかということです。std::vector はそうだと思いますが、それ以外ではよくわかりません。私はこのようにストレージを再利用するクラスを書いたことがありませんが、文字列やベクターを含むクラスはたくさん書いています。Herbのアドバイスに従えば、ストレージを再利用しないクラスでも問題ありません。最初はシンク関数のコピーバージョンでコピーを行い、もしコピーがパフォーマンスヒットになりすぎると判断したら、r-value参照のオーバーロードを作ってコピーを回避します(std::stringの場合と同じように)。一方、std::stringやその他のストレージを再利用する型では、quot;copy-and-move"を使うとパフォーマンスヒットを実証しており、おそらくほとんどの人のコードでこれらの型が多く使用されているはずです。私は今のところ Herb のアドバイスに従っていますが、この問題が完全に解決したと考える前に、もう少し考え抜く必要があります (おそらく、このすべてに書く時間のないブログ投稿が潜んでいるはずです)。
関連
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み] クラスにデフォルトコンストラクタが存在しない。
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】'cout'は型名ではない
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] ビットフィールドに値を代入しても、同じ値が戻ってこないのはなぜですか?
最新
-
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 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】変数 '' を抽象型 '' と宣言できない。
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】浮動小数点数の乱数生成
-
[解決済み】 while(cin) と while(cin >> num) の違いは何ですか?)
-
[解決済み] 参照渡しより値渡し、std::moveの方が優れている点
-
pass-by-valueとthen-moveの構成は悪いイディオムなのか?