C++11 で T&& (ダブルアンパサンド) はどういう意味ですか? 質問する

C++11 で T&& (ダブルアンパサンド) はどういう意味ですか? 質問する

私は C++11 の新機能のいくつかを調べてきましたが、変数を宣言するときに のように二重のアンパサンドが使用されることに気付きましたT&& var

まず、この怪物は何と呼ばれているのでしょうか? Google がこのような句読点の検索を可能にしてくれるといいのですが。

それは正確にはどういう意味ですか?

一見すると、二重参照(C スタイルの二重ポインターのようなT** var)のように見えますが、その使用例を考えるのは難しいです。

ベストアンサー1

それは宣言する右辺値参照(標準提案ドキュメント)。

右辺値の紹介です参照

マイクロソフトの標準ライブラリの1つによる右辺値参照の詳細な説明はこちら開発者

注意: MSDN のリンクされた記事 (「Rvalue 参照: VC10 の C++0x 機能、パート 2」) は、Rvalue 参照の非常にわかりやすい紹介ですが、Rvalue 参照に関する記述は、C++11 標準の草案ではかつては正しかったものの、最終版では正しくありません。具体的には、さまざまな箇所で、rvalue 参照は lvalue にバインドできると書かれていますが、これはかつては正しかったのですが、変更されました。(例: int x; int &&rrx = x; は、GCC でコンパイルされなくなりました) – drewbarbs 2014 年 7 月 13 日 16:12

C++03 参照 (C++11 では lvalue 参照と呼ばれるようになりました) との最大の違いは、const でなくても一時値のように rvalue にバインドできることです。したがって、次の構文は合法になりました。

T&& r = T();

右辺値参照は主に次の機能を提供します。

移動セマンティクス。通常の const-lvalue 参照の代わりに rvalue 参照を取る移動コンストラクタと移動代入演算子を定義できるようになりました。移動はコピーのように機能しますが、ソースを変更しないという義務はありません。実際、通常はソースを変更して、移動されたリソースを所有しないようにします。これは、特に標準ライブラリ実装で、余分なコピーを排除するのに最適です。

たとえば、コピー コンストラクターは次のようになります。

foo(foo const& other)
{
    this->length = other.length;
    this->ptr = new int[other.length];
    copy(other.ptr, other.ptr + other.length, this->ptr);
}

このコンストラクタに一時オブジェクトが渡された場合、一時オブジェクトは破棄されることがわかっているため、コピーは不要になります。一時オブジェクトがすでに割り当てたリソースを利用しないのはなぜでしょうか。C++03 では、一時オブジェクトが渡されたかどうかを判断できないため、コピーを防ぐ方法はありません。C++11 では、移動コンストラクタをオーバーロードできます。

foo(foo&& other)
{
   this->length = other.length;
   this->ptr = other.ptr;
   other.length = 0;
   other.ptr = nullptr;
}

ここでの大きな違いに注意してください。移動コンストラクタは実際に引数を変更します。これにより、一時的なオブジェクトが構築中のオブジェクトに効果的に「移動」され、不要なコピーが排除されます。

移動コンストラクタは、一時変数と、関数を使用して明示的に右辺値参照に変換される非定数左辺値参照に使用されます(変換のみを実行します)。次のコードは、とstd::moveの移動コンストラクタを呼び出します。f1f2

foo f1((foo())); // Move a temporary into f1; temporary becomes "empty"
foo f2 = std::move(f1); // Move f1 into f2; f1 is now "empty"

完全な転送。右辺値参照を使用すると、テンプレート関数の引数を適切に転送できます。次のファクトリ関数を例に挙げます。

template <typename T, typename A1>
std::unique_ptr<T> factory(A1& a1)
{
    return std::unique_ptr<T>(new T(a1));
}

を呼び出した場合factory<foo>(5)、引数は と推定され、のコンストラクタが を取っint&てもリテラル 5 にバインドされません。代わりに を使用することもできますが、 が非 const 参照によってコンストラクタ引数を取る場合はどうなるでしょうか。本当に汎用的なファクトリ関数を作成するには、 factory を と でオーバーロードする必要があります。factory が 1 つのパラメータ型を取る場合はこれで問題ないかもしれませんが、パラメータ型が追加されるたびに、必要なオーバーロード セットが 2 倍になります。これではすぐに保守不可能になります。foointA1 const&fooA1&A1 const&

rvalue参照は、標準ライブラリがstd::forwardlvalue/rvalue参照を適切に転送できる関数を定義できるようにすることでこの問題を解決します。動作の詳細についてはstd::forwardこの素晴らしい答え

これにより、次のようにファクトリ関数を定義できます。

template <typename T, typename A1>
std::unique_ptr<T> factory(A1&& a1)
{
    return std::unique_ptr<T>(new T(std::forward<A1>(a1)));
}

これで、引数の右辺値/左辺値がTのコンストラクタに渡されるときに保持されるようになりました。つまり、factory が右辺値で呼び出されると、Tのコンストラクタも右辺値で呼び出されます。factory が左辺値で呼び出されると、Tのコンストラクタも左辺値で呼び出されます。改良された factory 関数は、1 つの特別なルールに基づいて機能します。

関数パラメータの型が の形式であり、 がテンプレートパラメータであり、関数引数が 型の左辺値である場合T&&TAA&テンプレート引数の推論に使用されます。

したがって、ファクトリーを次のように使用できます。

auto p1 = factory<foo>(foo()); // calls foo(foo&&)
auto p2 = factory<foo>(*p1);   // calls foo(foo const&)

重要な右辺値参照プロパティ:

  • オーバーロードの解決では、lvalues は lvalue 参照へのバインディングを優先し、rvalues は rvalue 参照へのバインディングを優先します。そのため、一時オブジェクトでは、コピー コンストラクター/代入演算子よりも、移動コンストラクター/移動代入演算子の呼び出しが優先されます。
  • 右辺値参照は、右辺値と、暗黙的な変換の結果である一時変数に暗黙的にバインドされますfloat f = 0f; int&& i = f;つまり、float は暗黙的に int に変換可能であるため、適切に形成されます。参照は、変換の結果である一時変数への参照になります。
  • 名前付き右辺値参照は左辺値です。名前なし右辺値参照は右辺値です。std::moveこれは、次の場合に呼び出しが必要な理由を理解するために重要です。foo&& r = foo(); foo f = std::move(r);

おすすめ記事