これは参照の参照ではなく、プログラムの他の場所で参照されておらず、破壊的に変更できるメモリ内のオブジェクトへの参照を(非公式に)表す右辺値参照と呼ばれる新しい言語機能です。たとえば、関数の戻り値は、式に導入された一時的な値と同様に、右辺値参照によって取得できます。
右辺値参照は、さまざまな目的に使用できます。ほとんどのC++プログラマーの観点からは、これらを使用して移動セマンティクスを実装できます。これにより、古いオブジェクトの内容を古いオブジェクトから新しいオブジェクトに「移動」することで、新しいオブジェクトを初期化できます。これを使用すると、オブジェクトをコピーするために莫大な費用をかけることなく、C ++ 11の関数から巨大なオブジェクトを返すことができます。これは、戻り値のキャプチャに使用されるオブジェクトが、一時オブジェクトから内部を盗むだけでmoveコンストラクターを使用して初期化できるためです。 returnステートメントによって作成されます。
移動セマンティクスはコピーセマンティクスと直交しているため、オブジェクトはコピー可能でなくても移動可能です。たとえば、std::ofstream
sはコピーできませんが、移動可能であるためstd::ofstream
、移動動作を使用して関数からsを返すことができます。現在、これはC++03では実行できません。たとえば、このコードはC ++ 03では違法ですが、C ++ 11では完全に問題ありません(推奨されています)。
std::ifstream GetUserFile() {
while (true) {
std::cout << "Enter filename: ";
std::string filename;
std::getline(std::cin, filename);
ifstream input(filename); // Note: No .c_str() either!
if (input) return input;
std::cout << "Sorry, I couldn't open that file." << std::endl;
}
}
std::ifstream file = GetUserFile(); // Okay, move stream out of the function.
直感的には、右辺値参照を受け取る関数は、(おそらく)古いオブジェクトの内容を新しいオブジェクトに移動することによって高価なコピーを回避しようとしている関数です。たとえば、ベクトルのようなオブジェクトの移動コンストラクターを定義するには、そのコンストラクターに右辺値参照を取り込むことができます。ベクトルを配列へのポインター、配列の容量、および使用済みスペースのトリプルとして表す場合、次のようにその移動コンストラクターを実装できます。
vector::vector(vector&& rhs) {
/* Steal resources from rhs. */
elems = rhs.elems;
size = rhs.size;
capacity = rhs.capacity;
/* Destructively modify rhs to avoid having two objects sharing
* an underlying array.
*/
rhs.elems = nullptr; // Note use of nullptr instead of NULL
rhs.size = 0;
rhs.capacity = 0;
}
rhs
コンストラクターの最後でクリアすると、最終的に次のrhs
ような状態になることに注意することが重要です。
- デストラクタが呼び出したときにクラッシュを引き起こしません(
nullptr
解放nullptr
は安全なので、要素ポインタをに設定していることに注意してください)。
- それでも、オブジェクトに新しい値を割り当てることができます。この後者の点は注意が必要ですが、ある時点で、クリアされたオブジェクトに新しい値を与えることができるようにすることが重要です。これは、プログラムの後半で引き続き参照できるオブジェクトへの右辺値参照を取得できるためです。
(2)に光を当てるために、右辺値参照の興味深いユースケースの1つは、オブジェクト間で値を明示的に移動する機能です。たとえば、次の慣用的な実装について考えてみますswap
。
template <typename T> void swap(T& lhs, T& rhs) {
T temp = lhs;
lhs = rhs;
rhs = temp;
}
このコードは合法ですが、少し珍しいものです。特に、3つのコピーを作成することになります。最初temp
はのコピーに等しい設定lhs
、1つはのコピーに設定、もう1つlhs
はのコピーにrhs
設定rhs
する場合ですtemp
。しかし、ここではコピーを作成したくありません。代わりに、値をシャッフルしたいだけです。std::move
したがって、C ++ 11では、次の関数を使用して、オブジェクトへの右辺値参照を明示的に取得できます。
template <typename T> void swap(T& lhs, T& rhs) {
T temp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
現在、コピーはまったく作成されていません。の内容をに移動lhs
しtemp
、次にの内容をに移動rhs
しlhs
、次にの内容をに移動temp
しrhs
ます。そうすることで、新しい値を入れる前に、一時的に両方lhs
と「空」の状態のままにしました。rhs
オブジェクトからコンテンツを移動するコードを作成するときは、このコードが正しく機能するように、オブジェクトをある程度整形式の状態のままにしておくことが重要です。