18

次のコードが有効な理由:

template<typename T1>
void foo(T1 &&arg) { bar(std::forward<T1>(arg)); }

std::string str = "Hello World";
foo(str); // Valid even though str is an lvalue
foo(std::string("Hello World")); // Valid because literal is rvalue

だがしかし:

void foo(std::string &&arg) { bar(std::forward<std::string>(arg)); }

std::string str = "Hello World";
foo(str); // Invalid, str is not convertible to an rvalue
foo(std::string("Hello World")); // Valid

例 2 の左辺値が例 1 と同じ方法で解決されないのはなぜですか?

また、なぜ標準は引数の型を単純に推測するのではなく、 std::forward で引数の型を提供する必要があると感じているのでしょうか? タイプに関係なく、単に前に呼び出すことは意図を示しています。

これが標準的なものではなく、単なる私のコンパイラである場合、私は msvc10 を使用しています。

ありがとう

編集 1: リテラル "Hello World" を std::string("Hello World") に変更して右辺値を作成しました。

4

1 に答える 1

16

まず、これを読んで、転送の完全なアイデアをつかんでください。(はい、私はこの回答のほとんどを他の場所に委任しています。)

要約すると、転送とは、左辺値が左辺値のままで、右辺値が右辺値のままであることを意味します。1種類では無理なので2種類必要です。したがって、転送される引数ごとに、その引数に 2 つのバージョンが必要であり、関数には合計2 Nの組み合わせが必要です。関数のすべての組み合わせをコーディングできますが、テンプレートを使用すると、これらのさまざまな組み合わせが必要に応じて生成されます。


次のように、コピーと移動を最適化しようとしている場合:

struct foo
{
    foo(const T& pX, const U& pY, const V& pZ) :
    x(pX),
    y(pY),
    z(pZ)
    {}

    foo(T&& pX, const U& pY, const V& pZ) :
    x(std::move(pX)),
    y(pY),
    z(pZ)
    {}

    // etc.? :(

    T x;
    U y;
    V z;
};

次に、停止して次のようにする必要があります。

struct foo
{
    // these are either copy-constructed or move-constructed,
    // but after that they're all yours to move to wherever
    // (that is, either: copy->move, or move->move)
    foo(T pX, U pY, V pZ) :
    x(std::move(pX)),
    y(std::move(pY)),
    z(std::move(pZ))
    {}

    T x;
    U y;
    V z;
};

必要なコンストラクターは 1 つだけです。ガイドライン: データの独自のコピーが必要な場合は、パラメーター リストでそのコピーを作成します。これにより、呼び出し元とコンパイラにコピーまたは移動する決定が可能になります。

于 2011-12-01T23:09:38.607 に答える