タイトルに記載されているように、を除いた特定のクラスメソッドがとの移動演算子を移動する有効な理由/例があるかどうか、またはfree-functionは入力パラメータとしてR値参照。メソッドが入力としてR値参照を取る有効な理由はありますか?
答えて
シンク引数をモデル化するには2通りの方法があります。
最初は値による取ることによってである:
void foo(std::string);
第二は、右辺値参照によってである:発信者のための作業を簡素化するconst&
過負荷などの可能な変形と
void foo(std::string&&);
。
inline void foo(std::string const& s){
auto tmp = s;
return foo(std::move(tmp));
}
テイクシンクごとの値&&
とconst&
によってそれを取って(または手動で非一時的な値をコピーして、自分自身でそれを移動するために、発信者を必要とする)上に単一std::move
の余分なオーバーヘッドを有します。 2番目の過負荷は必要ありません。
したがって、1つの動きが考慮に値する場合は、シンクの引数をconst&
と&&
で取ると、移動を節約できます。さらに、コピーが余分に高価な場合は、コールサイトでそれを厄介にすることができ、したがってそれを抑止することができます。
しかしこれだけではありません。場合によっては、値が左辺値か左辺値かを検出し、右辺値の場合にのみコピーしたいことがあります。
例として、範囲アダプタbackwards
があるとします。 backwards
は適切な範囲をとります(for(:)
以上あり、その反復子を逆にすることができます)。逆方向に反復する範囲を返します。
単純に、あなたがしなければならないすべては、リバースイテレータを作成し、それらを格納、その後、あなたのソースの範囲からbegin
とend
を取得し、独自のbegin
とend
方法からそれらを返すです。
悲しいことに、これが壊れる:get_some_ints
から戻された一時の寿命がfor(:)
ループによって拡張されていないため、
std::vector<int> get_some_ints();
for(int x : backwards(get_some_ints())) {
std::cout << x << "\n";
}
!
{
auto&& __range_expression = backwards(get_some_ints());
auto __it = std::begin(__range_expression);
auto __end = std::end(__range_expression);
for (; __it != __end; ++__it) {
int x = *__it;
std::cout << x << "\n";
}
}
は(いくつかの小さな嘘は上記の子供たちに語っありますが、それはこの議論のために十分に近いです):
for(:)
それはおよそに展開されます。
このライン:
auto&& __range_expression = backwards(get_some_ints());
backwards
の戻り値は、長寿命です。その議論の生涯はそうではありません!
backwards
がR const&
の場合、vector
はループの前でサイレントに破棄され、関係するイテレータは無効です。
したがってbackwards
には、上記のコードを有効にするためにvector
のコピーを保存する必要があります。これは、ベクターを最後まで長く作る唯一の機会です!より従来に一方
auto some_ints = get_some_ints();
for(int x : backwards(some_ints)) {
std::cout << x << "\n";
}
some_ints
の余分なコピーを保存するには、恐ろしいアイデアとはかなり予想外になります。
この場合、backwards
は引数がrvalueかlvalueかを検出する必要があり、rvalueの場合はそれをコピーして戻り値に格納する必要があり、lvalueならばイテレータまたはそれへの参照を格納するだけです。
値を保持するときに不要なコピーを避ける。私はこの機能を"シンク関数"と呼んでいます。これは、セッターを定義するときに非常に頻繁に発生します。
class A
{
private:
std::string _s;
public:
void setS(const std::string& s) { _s = s; }
void setS(std::string&& s) { _s = std::move(s); }
};
int main()
{
A a;
std::string s{"some long string ......."};
a.setS(s); // copies and retains `s`
a.setS(std::move(s)); // moves and retains `s`
}
は、私は、それを比較することより面白いのconstのstd ::文字列& ''と 'のstd ::文字列&& S'を比較するのではなく考えます'std :: string s'を値で取るだけです。これは、通常、 'std :: string &&'を念頭に置き換えた代替手段です。 –
も参照してください:http://stackoverflow.com/questions/37935393/pass-by-value-vs-pass-by-rvalue-reference –
@ChrisBeck:はい、それは2つのオーバーロードを持つほど効率的ではありません。このオンライン上の多くの議論があります:http://blogs.microsoft.co.il/sasha/2014/08/21/c-sink-parameter-passing/ –
時には、std::vector
ような大規模なものの所有権を取得したいが、あなたは、事故によるコピーを作成することを回避したいです。唯一のr値の参照を提供することにより
は明示的に行う必要がありますコピーを渡したい、発信者をオーバーロード:
class DataHolder {
std::vector<double> a;
std::vector<int> b;
public:
DataHolder(std::vector<double>&& a, std::vector<int>&& b) : a(a), b(b) {}
};
auto a1 = makeLotsDoubles();
auto b1 = makeLotsInts();
DataHolder holder(std::move(a1), std::move(b1)); // No copies. Good.
auto a2 = makeLotsDoubles();
auto b2 = makeLotsInts();
DataHolder holder(a2, b2) // Forgot to move, compiler error.
あなたがし忘れた場合に代わりに、あなたはその後、値渡しを使用していた場合左辺値にstd::move
を使用してコピーを作成します。
コピー不可能なオブジェクトをクラスに転送すると便利です。一部のオブジェクトにはコピーコンストラクタがありません。つまり、値渡しできません。 RAIIでは、コピーを作成することで新たなリソースが得られます。あるいは、一般に、コピーコンストラクタおよび/またはデストラクタがオブジェクトの外に(デ)構築された副作用を持つ場合。 std :: stringをコピーするなど、コピーを作成するだけでは効率的ではないかもしれませんが、不可能です。
このようにオブジェクトを渡す方法の1つは、ポインタとして渡すことです。たとえば:コピーを作成しても意味がありません、元と同じファイルを削除と同じように
// Creates a file on construction, deletes on destruction
class File;
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(logfile1); // OK
logger.add_output(&logfile2); // BAD!
}
File
は、何のコピーコンストラクタを持っていません。コピーを避けるため、ファイルへのポインタをlogger
オブジェクトに渡します。 logfile1
でそれはそれが完了したときに、logger
がファイルを削除すると仮定して、うまく動作します。しかし、logfile2
には2つの大きな問題があります。 1つは、new
で割り当てられていないため、logger
は削除できません。もう1つは、logfile2
がブロックされ、ファイルを削除し、logfile2
がブロックの最後にスコープを残したときに、logger
に保存されたポインタを無効にすることです。
我々はFile
に先のファイルへのファイルの所有権を譲渡し、ソースファイルの「空」になるだろう移動のコンストラクタを、与えることができます。これで、上記のコードを動作させることができます。
{
// Create files
File* logfile1 = new File("name1");
File logfile2("name2");
// Give files to logger to use
logger.add_output(std::move(*logfile1));
logger.add_output(std::move(logfile2));
logger.add_output(File("name3"));
delete logfile1;
}
現在、新規で、またはローカルオブジェクトとして、あるいは無名のPR-値としてFile
を作成することができます。 std::move
を使用すると、コールサイトでFile
の所有権がlogger
に転送されることが明らかになります。ポインタの所有権移転がはっきりと示されていないので、セマンティクスを文書化し、そのドキュメンテーションをチェックすることによるものである(logger.add_output()
)。
コピーコンストラクタを持たないオブジェクトは、移動コンストラクタを持っていれば値渡しできます:http://melpon.org/wandbox/permlink/YYmU43P1ZLKlLJKx –
- 1. QSpinBoxは有効な値としてNaNを入力します
- 2. Rが古い.Rmdファイルを参照している理由
- 3. ASP.NETキャッシュを参照すると、有効期限切れの目的または理由は何ですか?
- 4. IPython.core.error.UsageError:GUIを有効にするには、return_control_callbackを参照として指定する必要があります。
- 5. JS文字列がそれ自身と等しくない理由はありますか? (写真を参照)
- 6. 反復子が左辺値として逆参照されない理由
- 7. メソッド内でのみ前方参照が可能な理由
- 8. これは戻り値の参照の有効な使用ですか?
- 9. SharePoint 2010の有効化時に参照フィールドに入力します
- 10. VBA入力したオブジェクトが有効なレコードセットプロパティではありません
- 11. jenkinsジョブを有効にしてユーザー入力変数を読み取る方法はありますか。
- 12. Rack :: Deflater gzipがRuby on Railsでデフォルトで有効になっていない理由はありますか?
- 13. Access 2010 - フォームフィールドへの参照をテキストフィールドの値として入力
- 14. サービスブローカがRESTORE DATABASEよりも無効になる理由はありますか
- 15. l値参照とr値参照におけるバインディングエラー
- 16. 'this'がグローバルオブジェクトではないオブジェクト 'obj'を参照する理由
- 17. Jqueryはnull値のDOM入力を参照します
- 18. Pythonで有効なユーザー入力を保証するために再帰を使用しない理由はありますか?
- 19. 有効な入力を入力しているときにタイプフォームが送信していませんか?
- 20. iPhone - 参照が(まだ)有効かどうかを知る方法はありますか?
- 21. 入力補完が参照(&)または基準値参照(&&)であるかどうかを知ることはできますか?
- 22. JavaScript RegExpはすべての有効な入力と無効な入力でfalseを返します
- 23. slim frameworkメソッドnotFoundは有効なメソッドではありません
- 24. Javaでメソッド参照を渡す方法はありますか?
- 25. JavaScriptのコードで入力値を取得できない理由
- 26. 文字列入力によるメソッドまたはプロパティの参照
- 27. メソッドは、値への参照配列を参照しています
- 28. 関連フィールド無効な参照があります:存在
- 29. Image1のプロパティピクチャに無効なファイル参照があります
- 30. 匿名クラスは外部参照を有効にしますか?
なぜあなたは無料の機能ではなく、メンバ関数を除外するのでしょうか?違いは何ですか? –
リソースを管理するクラスはどうなりますか?右辺値参照によって管理対象リソースのインスタンスを取ることができるので、中間の人を必要とせずにそのリソースを返す操作の結果を直接渡すことができます。 –
@ChristianHackl私は自由関数を除外しませんでした。私は単純に移動コンストラクタと移動代入演算子を除外しました。 – FdeF