右辺値参照を理解するのに問題を抱えている人がたくさんいます。「通常の」C++ コード (たとえば、標準ライブラリを使用するが、それを実装していない) は、新しい標準が登場したら右辺値参照について知る必要がありますか、それとも人々/コードは右辺値参照が存在しないふりをすることができますか? 右辺値参照は、主にライブラリの実装者またはすべてのプログラマに関係しますか?
8 に答える
この件に関する GMan (別名 gmannickg) のブログ記事では、異論を唱えるものはあまり見当たりませんでした。
http://blackninjagames.com/?p=95
要約: コピー アンド スワップ (および一般的には 3 のルール)について知っている人は、移動セマンティクス (したがって右辺値参照) について知っておく必要があります。したがって、それは C++ を少しでも気にかけている人だと思います。
知っておく必要はないと思います。もちろん、コピー アンド スワップは引き続き機能し、例外セーフであるなどの理由からです。また、「通常の」C++ コードとプログラマーがコピー アンド スワップを知る必要があるかどうかについて、誰かが議論する可能性があると思います。答えが「まあ、まあ、当然」であることを願っていますが、あなたにはわかりません.
もちろん、C++0x を使い始めるポイントは場所によって異なります。すぐに C++03 の学習をやめるのは、おそらく良い考えではありません。
たとえば、move 代入に依存できる場合は、コピー省略が無効なコンテキストで値によって巨大なコレクションを返す方が適切です。これは、C++03 で無効なものが C++0x で有効になるというケースではありません。C++03 では非常に遅いコードであり、回避する必要があります (適切に配置されている場合でもswap
) 。 、C++0xでうまくなりました。したがって、右辺値参照が存在しないふりをすると、必要のないことを回避し続けることになります。ジャンプすると、C++03 にクリーンにバックポートされる可能性がありますが、非常に遅く実行されます。これは苦痛です。
したがって、賢明なことは、それについて知ることかもしれません。多くの場合、それは存在しないふりをします。
少なくともほとんどの状況では、好きなだけ無視することができます。それらを使用するために標準ライブラリを (再) 作成すると、かなりの最適化といくつかの新しい機能が可能になりますが、主な目標の 1 つは、既存のコードが問題なく動作し続けることです (ただし、場合によってはより高速になります)。
既存のコードをサポートするということは、以前と同じ方法でコードを書き続けることができると感じている人なら誰でも影響を受けずに済むことを意味します (繰り返しますが、使用するライブラリの「もの」がより高速になる可能性があることを除いて)。
同時に、右辺値参照を簡単に使用できるようにするために、少なくともいくつかの経験則を学びたいというかなりの理由があります。明白な例の 1 つは、人々が現在定期的に遭遇する問題です。オブジェクトをコピーする意味がないクラスがありますが、それらのオブジェクトをコレクション (たとえば、ベクトル)。
そのクラスのオブジェクトを移動可能にするがコピー可能にしないことで、それを行うことができます。この代表的な例はストリームです。現在、どの種類の iostream オブジェクトもコレクションに入れることはできませんが、C++0x ではできるようになります。
もう 1 つの可能性は、「完全転送」を使用することです。これにより、以前はいくつかの個別の機能であったものを、実際の作業を行うものに転送する非常に単純なフロントエンドを持つ 1 つに統合することがかなり簡単になります。これはいつでもなくてもかまいませんが、場合によっては、コードの繰り返しがかなり少なくなることがあります。
ほとんどの人は、それらが存在しないふりをすることができ、利点 (場合によってはコードが高速になる) を理解するだけです。
C++0x には 99.9% の後方互換性があるため、既存のコードはすべて引き続き機能します。つまり、C++0x コードを C++03 のように書き続けることができます。
右辺値参照の利点を享受したい場合は、もちろんそれらについて学ぶ必要がありますが、ほとんどの場合、それはライブラリ作成者だけの関心事です。
右辺値参照を受け取る関数を作成するという意味では、右辺値参照について知る必要はないかもしれませんが、移動セマンティクスを理解すること、移動コンストラクターとは何かなどを理解することから利益が得られる場合があります。unique_ptr を使用したい場合 (参照カウントがないため、パフォーマンスの利点を得たい場合) は、ほぼ確実に std::move を使用する必要があります (たとえば、コレクションへの出入り)。 「ここに & または * を入力するとどうなるか」典型的な 2 年生がポインタに対処する方法。
ベルリンの Tech Ed Europe で「C++0x をフィーチャーした VC++ の新機能」の講演を行ったところ、右辺値参照に関する 1 つのスライドがあり、基本的に「これらは STL を高速化するため、ここにあることを知っておく必要があります」と述べました。しかし、それ以上掘り下げませんでした。これは、私が他の講演で行った (より長い) 変種よりも、参加者に受け入れられたようです。
私は、RValue 参照がすべての人のお茶のカップではないことに同意します。カップはかなり大きいです。
このため、C++0x の学習者には、開始してRValue 参照を利用する最善の方法は、STL を使用し、その設計パターンを時間の経過とともに受け入れることであると伝えています。これにより、事前に RValue 参照を完全に理解していなくても、 RValue 参照の恩恵を受ける可能性が非常に高くなります。
Scott Meyers は、C++0x の機能を次のように分類しました。
- みんなのための機能。
- クラス作成者向けの機能。
- ライブラリ作成者向けの機能。
RValue は間違いなくライブラリの作成者です。
C++11 では、「格納」または「消費」する予定のものはすべて && で渡します。これにより、最も効率的なコードが得られますが、左辺値の互換性が「壊れ」、すべてのメソッドの 2 つ (またはそれ以上) のバージョンを持つことを検討せざるを得なくなります。残念ながら、引数の数が増えると rvalue と const& の順列が急増し、関数ポインターの取得がより「醜い」ものになります。
左辺値用と右辺値用の 2 つ (またはそれ以上) のオーバーロードを提供する代わりに、右辺値のオーバーロードのみを提供することにしました。ユーザーが左辺値を渡したい場合は、std::ref()、std::cref でラップします。 ()、std::move()、またはコピーを明示的に作成し、左辺値を右辺値に変換する独自のヘルパー copy() を使用します。(std::ref() および std::cref() は、引数がテンプレート パラメーターである場合にのみ機能します...その場合、メソッドはおそらく std::forward<>() を使用して移動の自動検出を処理します) vsコピーセマンティクス。
template<typename T>
T copy( const T& v ) { return v; }
入力を「保存」または「消費」しないメソッドを作成している場合は、 const& を使用します
移動セマンティクスの恩恵を受けない型 (ディープ コピーなし) には const& を使用することを明確にする必要があります。しかし、テンプレート コードを書いているときは、型についてそのような仮定をしたくありません。
私が検討しているもう 1 つの方法は、特定のクラスを「明示的なコピーのみ」にすることです。
class test {
public:
test(){...};
test( test&& t ){...}
private:
template<typename T>
friend T copy(const T&t);
test& operator=(const test&); // not implemented
test( const test& t ){...}
std::vector<char> large_data;
};
int main( int argc, char** argv ) {
test x;
test y = copy(x);
test z = x; // error, test(const test&) is private...
return 0;
}
文字列、ベクトル、マップなどの「移動可能なメンバー」を含むすべてのクラスにこれを使用します。技術的には、それらをコピーできない理由はありませんが、可能な限り移動セマンティクスを試みるべきではない理由はありません。私は本当にコピーしたいですか?暗黙のコピーを使用すると、潜在的にコストがかかることを行っていることをコンパイラーが警告する方法がありません。
値セマンティクス クラスを実装していない場合は、実際に知る必要はありません。
編集:または完全な転送。しかし、それも同様にほとんどがライブラリの使用です。