113

のロジックを完全に理解していないことに気づきましたstd::move()

最初はグーグルで検索しましたが、構造がどのように機能するかではなく、使い方に関するドキュメントしかstd::move()ないようです。

つまり、テンプレートメンバー関数が何であるかは知っていますがstd::move()、VS2010の定義を調べると、それでも混乱を招きます。

std :: move()の定義は以下のとおりです。

template<class _Ty> inline
typename tr1::_Remove_reference<_Ty>::_Type&&
    move(_Ty&& _Arg)
    {   // forward _Arg as movable
        return ((typename tr1::_Remove_reference<_Ty>::_Type&&)_Arg);
    }

私にとって最初に奇妙なのは、パラメーター(_Ty && _Arg)です。これは、以下に示すように関数を呼び出すと、

// main()
Object obj1;
Object obj2 = std::move(obj1);

それは基本的に等しい

// std::move()
_Ty&& _Arg = Obj1;

しかし、すでにご存知のように、LValueをRValue参照に直接リンクすることはできません。そのため、このようにする必要があると思います。

_Ty&& _Arg = (Object&&)obj1;

ただし、std :: move()はすべての値に対して機能する必要があるため、これはばかげています。

したがって、これがどのように機能するかを完全に理解するには、これらの構造体も確認する必要があります。

template<class _Ty>
struct _Remove_reference
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&>
{   // remove reference
    typedef _Ty _Type;
};

template<class _Ty>
struct _Remove_reference<_Ty&&>
{   // remove rvalue reference
    typedef _Ty _Type;
};

残念ながら、それはまだ混乱を招き、私にはわかりません。

これはすべて、C++に関する基本的な構文スキルが不足しているためです。これらがどのように機能するのかを知りたいのですが、インターネットで入手できるドキュメントは大歓迎です。(これを説明できれば、それも素晴らしいでしょう)。

4

2 に答える 2

184

まず、move関数(少しクリーンアップしました)から始めます。

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
  return static_cast<typename remove_reference<T>::type&&>(arg);
}

簡単な部分から始めましょう。つまり、関数が右辺値で呼び出される場合です。

Object a = std::move(Object());
// Object() is temporary, which is prvalue

テンプレートは次のようにmoveインスタンス化されます。

// move with [T = Object]:
remove_reference<Object>::type&& move(Object&& arg)
{
  return static_cast<remove_reference<Object>::type&&>(arg);
}

にまたはにremove_reference変換T&され、参照ではないため、最終的な関数は次のようになります。TT&&TObject

Object&& move(Object&& arg)
{
  return static_cast<Object&&>(arg);
}

さて、あなたは不思議に思うかもしれません:私たちはキャストさえ必要ですか?答えは:はい、そうです。理由は簡単です。名前付き右辺値参照左辺値として扱われます(左辺値から右辺値参照への暗黙の変換は標準で禁止されています)。


movelvalueで呼び出すと次のようになります。

Object a; // a is lvalue
Object b = std::move(a);

および対応するmoveインスタンス化:

// move with [T = Object&]
remove_reference<Object&>::type&& move(Object& && arg)
{
  return static_cast<remove_reference<Object&>::type&&>(arg);
}

繰り返しますが、にremove_reference変換Object&するObjectと、次のようになります。

Object&& move(Object& && arg)
{
  return static_cast<Object&&>(arg);
}

ここで、トリッキーな部分Object& &&に行き着きます。それはどういう意味で、どのように左辺値にバインドできるのでしょうか。

完全な転送を可能にするために、C ++ 11標準には、参照の折りたたみに関する特別なルールが用意されています。これは次のとおりです。

Object &  &  = Object &
Object &  && = Object &
Object && &  = Object &
Object && && = Object &&

ご覧のとおり、これらのルールでは、Object& &&実際にはを意味Object&します。これは、左辺値をバインドできる単純な左辺値参照です。

したがって、最終的な機能は次のとおりです。

Object&& move(Object& arg)
{
  return static_cast<Object&&>(arg);
}

これは、以前の右辺値によるインスタンス化とは異なりません。どちらも引数を右辺値参照にキャストしてから返します。違いは、最初のインスタンス化は右辺値でのみ使用でき、2番目のインスタンス化は左辺値で機能することです。


remove_referenceもう少し必要な理由を説明するために、この関数を試してみましょう

template <typename T>
T&& wanna_be_move(T&& arg)
{
  return static_cast<T&&>(arg);
}

左辺値でインスタンス化します。

// wanna_be_move [with T = Object&]
Object& && wanna_be_move(Object& && arg)
{
  return static_cast<Object& &&>(arg);
}

上記の参照折りたたみルールを適用すると、使用できない関数が得られることがわかりますmove(簡単に言うと、左辺値を使用して呼び出すと、左辺値が返されます)。どちらかといえば、この関数は恒等関数です。

Object& wanna_be_move(Object& arg)
{
  return static_cast<Object&>(arg);
}
于 2011-09-22T16:39:37.183 に答える
4

_Tyはテンプレートパラメータであり、この状況では

Object obj1;
Object obj2 = std::move(obj1);

_Tyはタイプ「Object&」です

そのため、_Remove_referenceが必要です。

それはもっと

typedef Object& ObjectRef;
Object obj1;
ObjectRef&& obj1_ref = obj1;
Object&& obj2 = (Object&&)obj1_ref;

参照を削除しなかった場合は、実行していたようになります

Object&& obj2 = (ObjectRef&&)obj1_ref;

しかし、ObjectRef &&はObject&に還元され、obj2にバインドできませんでした。

このように減らす理由は、完全な転送をサポートするためです。このペーパーを参照してください。

于 2011-09-22T06:12:56.697 に答える