6

ユニバーサルリファレンスに関する講義で、Scott Meyers(約40分)は、ユニバーサルリファレンスであるオブジェクトは、使用する前に実際の型に変換する必要があると述べました。つまり、ユニバーサル参照型のテンプレート関数がある場合は、std::forward演算子と式を使用する前に使用する必要があります。そうしないと、オブジェクトのコピーが作成される可能性があります。

これについての私の理解は次の例にあります:

#include <iostream>

struct A
{
  A() { std::cout<<"constr"<<std::endl; }
  A(const A&) { std::cout<<"copy constr"<<std::endl; }
  A(A&&) { std::cout<<"move constr"<<std::endl; }
  A& operator=(const A&) { std::cout<<"copy assign"<<std::endl; return *this; }
  A& operator=(A&&) { std::cout<<"move assign"<<std::endl; return *this; }

  ~A() { std::cout<<"destr"<<std::endl; }

  void bar()
  {
    std::cout<<"bar"<<std::endl;
  }
};

A getA()
{
  A a;
  return a;
}

template< typename T >
void callBar( T && a )
{
  std::forward< T >( a ).bar();
}

int main()
{
  {
    std::cout<<"\n1"<<std::endl;
    A a;
    callBar( a );
  }

  {
    std::cout<<"\n2"<<std::endl;
    callBar( getA() );
  }
}

予想どおり、出力は次のとおりです。

1
constr
bar
destr

2
constr
move constr
destr
bar
destr

問題は、なぜこれが必要なのかということです。

std::forward< T >( a ).bar();

std :: forwardなしで試しましたが、正常に動作しているようです(出力は同じです)。

同様に、なぜ彼は右辺値を持つ関数内でmoveを使用することを推奨するのですか?(答えはstd :: forwardの場合と同じです)

void callBar( A && a )
{
  std::move(a).bar();
}

std::moveとは両方とも適切なタイプへのキャストであることを理解してstd::forwardいますが、これらのキャストは上記の例で本当に必要ですか?

ボーナス:その関数に渡されるオブジェクトのコピーを生成するために、例をどのように変更できますか?

4

4 に答える 4

2

bar()右辺値と左辺値で別々にオーバーロードされる可能性があるため、必要です。つまりa、左辺値または右辺値として正しく記述したか、単に左辺値のように盲目的に扱ったかに応じて、何かが異なるか、完全に許可されない可能性があることを意味します。現在、ほとんどのユーザーはこの機能を使用しておらず、ほとんどの一般的なコンパイラがサポートしていないため、公開されていません。GCC 4.8 でさえ rvalue をサポートしていません*this。でもスタンダードです。

于 2012-10-11T10:32:03.097 に答える
2

&&関数のパラメーターに対する には、2 つの異なる用途があります。通常の関数の場合、引数が右辺値参照であることを意味します。テンプレート関数の場合、右辺値参照または左辺値参照のいずれかになることができることを意味します。

template <class T> void f(T&&); // rvalue or lvalue
void g(T&&);                    // rvalue only
void g(T&)                      // lvalue only

void h() {
    C c;
    f(c);            // okay: calls f(T&)
    f(std::move(c)); // okay: calls f(T&&)
    g(c);            // error: c is not an rvalue
    g(std::move(c)); // okay: move turns c into an rvalue
}

and内fg、そのような引数に適用std::forwardすると、引数の左辺値または右辺値性が保持されるため、一般に、これが引数を別の関数に転送する最も安全な方法です。

于 2012-10-11T15:42:25.160 に答える
1
void callBar( A && a )
{
  std::move(a).bar();
}

パラメータとして右辺値参照があり、右辺値にのみバインドできる場合は、通常、移動セマンティクスを使用してこの右辺値から移動し、その根性を取り除きます。

パラメータ自体は名前付きのものであるため、左辺値です。あなたはそれのアドレスを取ることができます。

したがって、それを再び右辺値にして、そこから移動できるようにするために、それを適用std::moveします。文字通り、渡されたパラメーターで関数を呼び出しているだけの場合、右辺値参照であるパラメーターがある理由がわかりません。

関数内でこれから移動する場合にのみ、右辺値参照を渡したいので、を使用する必要がありますstd::move

ここでのあなたの例は、その点では実際にはあまり意味がありません。

于 2012-10-11T10:32:31.890 に答える