9

この質問から、移動セマンティクスとは何かを既に理解しています: 移動セマンティクスとは何ですか?

しかし、移動のセマンティクスに関して、完全な転送とは何かをまだ理解していません。

誰かが簡単な英語で簡単な例を説明して、完璧な転送が何を意味するかを説明できますか?

4

2 に答える 2

16

平易な英語のみの試み

この問題はおそらく複雑すぎて、平易な英語の文章で正確に説明することはできませんが、完全な転送は、最初の関数がまったく存在しないかのように、関数に渡された一時的な値を別の関数に移動する方法と考えることができます。不要なコピーまたは割り当て。C++11 では、参照 (右辺値または左辺値) を取得しようとするときに、型への右辺値 (&&) 参照と左辺値 (&) 参照の間にいくつかの変換規則を導入することで、これを行うことができます。そのうちの。

R 値参照は C++11 の機能であり、移動セマンティクスと完全な転送の問題の両方に対処するように設計されています

これは平易な英語の説明ですが、問題を完全に理解したい場合は、次を読むことをお勧めします。

問題:

関数に渡されたいくつかの一時的な値は、コピーや割り当てなしFで別の関数に渡される必要があります。E

この問題を解決しようとする試み

  • 次のように参照渡ししようとすると

    template<typename T> void F(T& a) { E(a); }
    

    一時値を使用することはできません (左辺値ではありません)。

    F(1, 2, 3); // Won't work
    
  • 参照を宣言するconstと、スタック上の一時オブジェクトの有効期間が延長されます (これは、一般的なダングリング参照エラーを回避するために歴史的に行われていました)。したがって、次のように機能します。

    template<typename T> void E(const T& a) {}
    
    template<typename T> void F(const T& a) {
        E(a);
    }
    

    ただし、欠点は、このソリューションに準拠するために関数のシグネチャを変更する必要があることです

  • E の署名 (何かに準拠する必要があります) に関心があり、F の署名には関心がない場合は、

    template<typename T> void E(T& a) {}
    
    template<typename T> void F(const T& a) {
        E(const_cast<T&>(a));
    }
    

    ただし、これが実際の constで呼び出され、定数化されていない場合、未定義の動作がトリガーされます

  • 保守不可能な解決策は、必要なすべてのバリアントを定義することです

    template<typename T> void E(T& a) {}
    
    template<typename T> void F(T& a) { E(a); }
    template<typename T> void F(const T& a) { E(const_cast<T&>(a)); }
    

    ただし、パラメーターの数が増えると、組み合わせの数も増えます。これは維持できなくなる可能性があります。

C++11 での解決策

C++11 では、次のような規則が定義されています。

「[与えられた] 型 T への参照である型 TR を作成しようとすると、型「cv TR への左辺値参照」を作成しようとすると、型「T への左辺値参照」が作成されますが、型「への右辺値参照」を作成しようとすると、 cv TR" はタイプ TR を作成します。"

人間の形 (TR = タイプ T への参照、R = 参照):

TR      R
T& & -> T&    // an lvalue reference to cv TR (becomes)-> lvalue reference to T 
T& && -> T&   // an rvalue reference to cv TR (becomes)-> TR (lvalue reference to T) 
T&& & -> T&   // an lvalue reference to cv TR (becomes)-> lvalue reference to T 
T&& && -> T&& // an rvalue reference to cv TR (becomes)-> TR (rvalue reference to T)

ここで重要なことは、関数が受け取った型を追跡できるようになったことです。左辺値を受け取って同じ左辺値を E に渡すか、右辺値を受け取って同じ右辺値を渡すことができます (型参照への左辺値参照が左辺値参照になるため、それを変換した後) E:

template<typename T> void E(T&& a) {}

template<typename T> void F(T&& a) { E(static_cast<T&&>(a)); }

シンタックスシュガー

static_cast<T&&>(a)

std::forward<T>(a); // is the same as static_cast<T&&>(a);

問題を解決し、あなたの人生を楽にする最終的なコードは

template<typename T> void E(T&& a) {}

template<typename T> void F(T&& a) { E(std::forward<T>(a)); }

実際の例


参考文献: Herb Sutter のブログと、残念ながらもう見つからないその他の情報源。誰かがそれらについての手がかりを持っている場合は、下のコメントに書き込んでください。投稿を更新します. ありがとう。

于 2014-07-14T09:46:39.107 に答える