auto&& var = <initializer>
あなたが言っていることを使用することによって:私はそれが左辺値または右辺値の式であるかどうかに関係なくすべての初期化子を受け入れ、その定数を保持します。これは通常、転送に使用されます(通常はでT&&
)。これが機能する理由は、「ユニバーサルリファレンス」auto&&
またはが何かT&&
にバインドされるためです。
あなたは言うかもしれませんが、それは何かにもバインドさconst auto&
れるので、なぜ単にaを使用しないのですか?参照を使用する際の問題は、それが!後でそれを非定数参照にバインドしたり、マークされていないメンバー関数を呼び出したりすることはできません。const
const
const
例として、を取得しstd::vector
、イテレータを最初の要素に移動し、そのイテレータが指す値を何らかの方法で変更するとします。
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
このコードは、初期化式に関係なく正常にコンパイルされます。auto&&
次の方法で失敗する代替案:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
したがって、これにauto&&
は完全に機能します!このように使用する例はauto&&
、範囲ベースのfor
ループです。詳細については、他の質問を参照してください。
次に、参照を使用std::forward
して、auto&&
元々左辺値または右辺値のいずれかであったという事実を保持すると、コードは次のようになります。左辺値または右辺値のいずれかの式からオブジェクトを取得したので、元々の値を保持したいと思います。持っていたので、私はそれを最も効率的に使用することができます-これはそれを無効にするかもしれません。のように:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
これによりuse_it_elsewhere
、元の初期化子が変更可能な右辺値であった場合に、パフォーマンス(コピーを回避)のためにその根性を取り除くことができます。
これは、リソースを盗むことができるかどうか、またはいつ盗むことができるかについて、どういう意味var
ですか?まあ、auto&&
意志は何かにバインドするので、私たちはおそらく自分自身で内臓を引き裂こうとすることはできませんvar
-それは非常によく左辺値または定数でさえあるかもしれません。ただしstd::forward
、内部を完全に破壊する可能性のある他の機能にそれを行うことはできます。これを行うとすぐvar
に、無効な状態にあると見なす必要があります。
auto&& var = foo();
次に、これを、質問で与えられたように、fooが値で返す場合に適用してみましょうT
。var
この場合、タイプはとして推定されることが確実にわかりT&&
ます。それが右辺値であることは確かにわかっているので、std::forward
そのリソースを盗むためにの許可は必要ありません。この特定のケースでは、それが値で返されることを知っているfoo
ので、読者はそれを次のように読む必要がありますfoo
。
some_expression_that_may_be_rvalue_or_lvalue
補遺として、 「コードが変わるかもしれない」という状況以外に、のような表現が現れる可能性がある場合は言及する価値があると思います。だからここに不自然な例があります:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
これは、ジェネリックget_vector<T>()
型に応じて左辺値または右辺値のいずれかになり得るその素敵な式T
です。get_vector
基本的に、のテンプレートパラメータを介しての戻りタイプを変更しますfoo
。
を呼び出すとfoo<std::vector<int>>
、get_vector
は値で戻りglobal_vec
、右辺値式が得られます。または、を呼び出すとfoo<std::vector<int>&>
、参照によってget_vector
返さglobal_vec
れ、左辺値式になります。
私たちがそうするなら:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
予想どおり、次の出力が得られます。
2
1
2
2
auto&&
コード内のを、、、、のいずれかにauto
変更auto&
しconst auto&
た場合const auto&&
、希望する結果が得られません。
auto&&
参照が左辺値または右辺値のどちらの式で初期化されているかに基づいてプログラムロジックを変更する別の方法は、型特性を使用することです。
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}