1. ホーム
  2. c++

[解決済み】auto&&は何を教えてくれるの?

2022-04-11 02:39:48

質問

のようなコードを読むと

auto&& var = foo();

ここで foo の型の値で返す任意の関数です。 T . そして var への参照であるrvalue型のlvalueです。 T . しかし、これが意味するのは var ? の資源を盗んでいいということでしょうか? var ? を使うべき合理的な場面はありますか? auto&& を返すときのように、コードの読者に何かを伝えることができます。 unique_ptr<> を使えば、排他的所有権を持っていることがわかるのでは?また、例えば次のような場合はどうでしょう。 T&& とすると T はクラス型ですか?

の他の使用例があるかどうか、理解したいだけです。 auto&& この記事の例で説明されているような、テンプレート・プログラミングにおけるものとは異なります。 ユニバーサルリファレンス Scott Meyersによるものです。

解決するには?

を使用することで auto&& var = <initializer> と言っているようなものです。 lvalue式かrvalue式かに関係なく、どんなイニシャライザーでも受け入れ、その定数性を保持します。 . これは通常 転送 (通常 T&& ). これがうまくいくのは、"universal reference"があるからです。 auto&& または T&& にバインドされます。 何でも .

と言うかもしれません。 const auto& なぜなら また 何にでもバインドするのか?を使うことの問題点は const の参照は、それが 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&& を参照し、それが元々 lvalue か rvalue のどちらかであったという事実を保持するように、あなたのコードに書かれています。 lvalueまたはrvalue式からオブジェクトを取得したので、最も効率的に使用できるように、元々持っていた値を保持したいのです - これは無効になるかもしれません。 というように。

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 は、元のイニシャライザーが変更可能なrvalueであった場合、パフォーマンスのため(コピーを避けるため)に、その内臓を引き裂くことができます。

からリソースを盗めるかどうか、いつ盗めるかについて、これは何を意味するのでしょうか。 var ? さて auto&& は何にでもバインドされるから、それを取り除くことはできない。 var の中身は、lvalue や const でさえあるかもしれません。しかし、私たちは std::forward を他の関数に渡すと、その内部を完全に破壊してしまうかもしれません。これを実行したら、すぐに var は無効な状態である。

では、これを応用して auto&& var = foo(); 質問で示されたように、foo は T を値で指定します。この場合、私たちは確実に var と推論されます。 T&& . rvalueであることは確実なので、このような場合は std::forward の許可を得て、そのリソースを盗むことができます。この具体的な事例では を知っていること foo は値で返す と読者は読めばいいのです。 から返された一時的なものへのrvalueの参照を取っている。 foo だから、そこから楽しく移動することができるんだ。


補足として、以下のような表現がある場合、言及する価値があると思います。 some_expression_that_may_be_rvalue_or_lvalue が出てくるかもしれません。それは、quot;well your code might change"という状況とは別のものです。ここでは、その例を挙げてみましょう。

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>() は、ジェネリックタイプによってL値にもR値にもなりうる素敵な式です。 T . の戻り値の型を変更するのです。 get_vector のテンプレートパラメータを通して foo .

を呼び出すと foo<std::vector<int>> , get_vector を返します。 global_vec を値で指定すると、rvalue 式が得られる。あるいは foo<std::vector<int>&> , get_vector を返します。 global_vec を参照し、lvalue 式を生成します。

そうすると

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&& 参照がlvalueまたはrvalue式で初期化されている場合、型特性を使用することができます。

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
}