名前を「値渡し」には、
ではC++98
、中央、値渡し(値渡し)非効率的な、大胆不敵なコピーを意味し、それはプログラマーの過半数をもって迎えられました。したがって、カスタムの種類のプッシュ、参考のために使用しなければならない場合にはpass-by-const-reference
、パラメータを使用する必要がある場合pass-by-reference
。
削除ムーブセマンティクス(move semantic)
でのC++98
標準は、そのような結論は理解できます。移動セマンティクスを増やすしかし、(move semantic)
下のC++11
標準は、いくつかの特別なシナリオでの値で優れた性能を持っている、それはの「値による」名までの時間です。
例
使用する2つがありますstd::function
の定義「模倣機能が。」
#include <string>
#include <functional>
using Matcher = std::function<bool(int)>;
using Action = std::function<std::string(int)>;
C ++ 98スタイル
さらにそこにあるAtom
関数オブジェクトは。既存によるとC++98
、使用の習慣pass-by-const-reference
パラメータを渡すには、呼び出す必要がありstd::function
、「コピーコンストラクタを。」
struct Atom {
Atom(const Matcher& matcher, const Action& action)
: matcher(matcher), action(action) {
}
std::string operator()(int m) const {
return matcher(m) ? action(m) : "";
}
private:
Matcher matcher;
Action action;
};
しかし、に渡されたときAtom
、コンストラクタであるstd::function
「正しい値」の種類は、我々はむしろより安いコール「モバイル構造」を楽しみにしてたときに「コピーコンストラクタ。」、1つの分析ずついくつかのソリューションがあります。
オーバーロード機能
以下からの値を修正することができ、オーバーロードコンストラクタを使用して、左または右に実現される機能は、値が割り当てられますが、二つのパラメータの重い負荷をサポートするために、4つのオーバーロードされたコンストラクタを達成する必要があります。組み合わせ爆発は、性能を向上させるために、このソリューションのコストは非常に高価支払った、典型的な設計上の欠陥です。
struct Atom {
Atom(const Matcher& matcher, const Action& action)
: matcher(matcher), action(action) {
}
Atom(const Matcher& matcher, Action&& action)
: matcher(matcher), action(std::move(action)) {
}
Atom(Matcher&& matcher, const Action& action)
: matcher(std::move(matcher)), action(action) {
}
Atom(Matcher&& matcher, Action&& action)
: matcher(std::move(matcher)), action(std::move(action)) {
}
std::string operator()(int m) const {
return matcher(m) ? action(m) : "";
}
private:
Matcher matcher;
Action action;
};
コスト分析
転送値が去ったとき、「コピーコンストラクタ」がある;右の値を渡す、「移動構造」があるが、場合組み合わせ爆発の問題は許容できないがあります。
モバイル参照
「モバイルリファレンス」の使用は、左と右の値の透過転送を実現するためのメカニズム「完璧な変換」を使用して4つのコンストラクタを、マージすることができます。しかし、「モバイル参照」の使用は、一般的な設計を導入し、その実装は依存コンパイル時のコストを増加させる、ヘッダファイルに配置するだけでなく、実装の複雑さを増加しなければなりません。クライアントが正しい型を渡さないときまた、100%ではない完璧な「完璧な変換」は、与えられた、コンパイラエラーメッセージは非常に長いです。
struct Atom {
template <typename Matcher, typename Action>
Atom(Matcher&& matcher, Action&& action)
: matcher(std::forward<Matcher>(matcher))
, action(std::forward<Action>(action)) {
}
std::string operator()(int m) const {
return matcher(m) ? action(m) : "";
}
private:
Matcher matcher;
Action action;
};
コスト分析
転送値が去ったとき、「コピーコンストラクタ」があり、正しい値を渡すとき、ある「モバイル構造。」これは、組み合わせ爆発の問題が、テンプレート、テンプレート及び膨張の問題の複雑さの導入を回避する、比較的オーバーロード方法は、コードがより簡潔です。
値渡し
使用するpass-by-value
モード転送Matcher
とAction
左シンプル、ナチュラルサポート値や正しい値だけでなく、コストが許容範囲内です。
struct Atom {
Atom(Matcher matcher, Action action)
: matcher(std::move(matcher))
, action(std::move(action)) {
}
std::string operator()(int m) const {
return matcher(m) ? action(m) : "";
}
private:
Matcher matcher;
Action action;
};
コスト分析
転送値が去ったとき、「コピーコンストラクタ」、および「モバイル構造」がある;右の値を渡したときに、二度ある「モバイル構造。」低コストの「モバイル構造」よりもコストがかかるが、完全に組み合わせ爆発の問題を回避し、テンプレートコードの複雑さより簡潔な2つのプログラムへの相対。
値によって渡された場合
要約すると、条件が「値渡し」と考えることができるの方法を用いて、以下のように要約することができます。
- プロデュースコピー;
- 種類は、コピー、
- 低移動費。
例えば、上述した関数オブジェクトはAtom
、完全に上記の要件を満たします。
- プロデュースコピー:プライベート生成するための「コピーコンストラクタ」または「モバイル構造」による
matcher
と、action
コピーを。 - タイプコピー:
std::function
タイプコピーします。 - 低コストの移動:
std::function
建設の移動のコストは非常に低いです。
ラムダの応用
ラムダは、単純化するために使用することができるAtom
実装クラスを。
using Rule = std::function<std::string(int)>;
Rule atom(Matcher matcher, Action action) {
return [matcher, action](int m) {
return matcher(m) : action(m) : "";
};
}
しかし、「パラメータキャプチャリスト」にして、matcher, action
それが避けられない場合は、コール閉鎖オブジェクトに値によって渡されるstd::function
の「コピーコンストラクタ。」幸いなことに、C++14
それはクロージャオブジェクトを移動するように初期化されるオブジェクトをサポートしています。
using Rule = std::function<std::string(int)>;
Rule atom(Matcher matcher, Action action) {
return [matcher = std::move(matcher), action = std::move(action)](int m) {
return matcher(m) : action(m) : "";
};
}