2011-12-01 9 views
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を使用しています。これは厄介なC++ 11サポートを説明します。

おかげ

編集1:はstdされるリテラルの "Hello World" に変更::文字列( "Hello World" の)右辺値を作成します。

+0

バーではどうなりますか?コンパイルが必ずしも機能しているとは限りません。私はそれが 'void foo(T1&arg)'と 'void foo(std :: string&arg)'であると信じています。 – AJG85

+1

'' Hello World "'は右辺値ではなく、 'const char [12] '型の左辺値です。 – GManNickG

+0

@ AJG85バーで起こることは重要ではありません。 &&は値の参照を意味します。 – Mranz

答えて

15

まず、read this転送の完全なアイデアを得るために。 (はい、私はこの回答のほとんどを他の場所に委託しています)。

要約すると、転送は、lvaluesがlvaluesのままで、rvaluesがrvaluesのままであることを意味します。 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つだけを必要としますコンストラクタ。 ガイドライン:データのコピーが必要な場合は、そのコピーをパラメータリストに作成します。これにより、呼び出し元とコンパイラーにコピーまたは移動する決定が可能になります。

+1

最初の例が動作するのは、T1が実際に 'void foo(const std :: string&amp; arg)'に相当するものに解決され、参照を使って 'void foo(const std :: string&arg)控除規則?例2では文字列の左辺値のオーバーロードがないので失敗しますか?テンプレート関数を定義するためのベストプラクティスがあるので、型がどんなものであるべきかが少なくとも比較的明らかです。理想的には、2^N過負荷を回避しながら型を明示的に表現する良い方法を探しています。 – Mranz

+1

@Mranz:正確に。さて、あなたは何のために行くのですか?フォワーディングとテンプレートは転送に使用するためのもので、実際にそのタイプを知る必要はありません。 – GManNickG

+0

私は 'Class(.. some args ...、ベクトル items)'のようなベクトルを取り込むコンストラクタを持っているとします。理想的には、私は移動のセマンティクスまたは右辺値を使用してコピーを避けるためにその引数を許可したいと思います。これをサポートするには、アイテムをテンプレートにするか、またはrvalueオーバーロードを作成する必要があります。私がそれをテンプレート化すると、実際の型は定義で失われ、実際の引数は、ヘッダファイルやコメントを読んでクラスのコンシューマによって推測されなければなりません。 – Mranz

関連する問題