2016-01-19 7 views
6

私はそうのように、多くのオーバーロードで与えられた機能を持っているファイルで働いています:私の一般的なテンプレートはTまたはT &&を使用すべきですか?

template<typename T> inline X f(T impl, W w) { 
    return f(impl, w->getY(), w->getZ()); 
} 

inline X f(ScriptWrappable* impl, Y y, Z z) { ... } 
inline X f(Node* impl, Y y, Z z) { ... } 
inline X f(RawPtr<T> impl, Y y, Z z) { ... } 
inline X f(const RefPtr<T>& impl, Y y, Z z) { ... } 
inline X f(ScriptWrappable* impl, Y y, Z z) { ... } 
inline X f(const String& impl, Y y, Z z) { ... } 
inline X f(int64_t impl, Y y, Z z) { ... } 
template<typename T, size_t capacity> inline X f(const Vector<T, capacity>& impl, Y y, Z z) { ... } 

私はそうのように、パラメータを1つだけ必要とする新しいオーバーロードを追加しようとしています

私は上記のすべてのバリエーションが新しい2パラメータバージョンで自動的に動作するようにテンプレートを使用しています。

しかし、コードレビューでは、「T&&はコピーを避ける方が良いですか?」と尋ねられました。つまり、代わりにする必要があります

template<typename T> inline X f(T&& impl, W w) { 
    return f(impl, w->getY(), w->getZ()); 
} 

私は本当にこの質問への答えを知りません。私は普遍的な参照を理解したと思ったが、彼らが良いアイデアであるか否かに慣れていない。自分の状況を他のものよりも選ぶことの結果はどうなりますか?

私は推測していた場合、私はTのためで立つことができるオリジナルのタイプは(プリミティブ、参照、またはポインタ)T &をコピーするすべての単純なので&がはるかに利益を与えることはありませんことを言うと思います。しかし、私はまだ興味がある。どのタイプもTバージョンに渡すことができなかったT & &バージョンに渡すことができ、その逆も可能です。

+0

呼び出し元が移動したい場合、引数を 'std :: move'するだけでよい。 'T && 'を使うことで、あなたは**彼にそれをやらせることを余儀なくされています。 'T'だけを使うことで、彼に選択肢を与えています。 –

+4

推測されたコンテキストでテンプレートを使用している場合、これは当てはまりません –

+0

ああ、それは分かりませんでした。説明をありがとう。 –

答えて

8

あなたはこのようなあなたの関数を記述する必要があります

template<typename T> inline X f(T&& impl, W w) { 
    return f(std::forward<T>(impl), w->getY(), w->getZ()); 
} 

これは完璧な転送と呼ばれています。 implのタイプは、fを直接呼び出した場合とまったく同じになります。 ではなく、は必ずr値参照になります。

+0

ありがとうございます!それは完璧なフォワーディングが私の意図によく合うように聞こえる。私は他の選択肢を選択した場合、何が起こるのかを理解したいと思っていますが、「完璧な転送」を検索するとhttp://eli.thegreenplace.net/2014/perfect-forwarding-and-universalのようなページが表示されます-references-in-c /助けてくれるようです。 – Domenic

+0

リチャード・ホッジスの答えを見てください。 –

+0

@TobiasBrandt、fがimplの所有権を取ることを意図していない限り、なぜconst T&を使用しないのですか? –

3

答えは、テンプレートに関するコンパイラの規則をいくらか理解する必要があるため、少し複雑です。次の宣言で

:Tがコンテキストを推定し、その結果としてで評価され

template<class T> void func(T&& t); 

は、コンパイラR値の参照またはL値基準としていずれかによって治療されますいずれか適切である。 std::forward(注:この場合、ではなく、std::move)の使用と組み合わせると、完璧な転送が行われ、最適に効率的になります。

しかし、これは推定コンテキストで評価されていない。この場合

template<class T> 
struct X { 
    void foo(T&& t); 
}; 

、fooがR値の参照を要求するという事実にあります。あなたはstd::moveを使用する必要があり、これらの2つの場合

template<class T> 
void foo(std::vector<T>&& v); 

はいずれも、この推定された文脈です。

+0

推論された文脈における 'T'の評価ではなく、r値またはl値参照のどちらかになります。 'typedef int & T; typedef T && X;'には推定コンテキストがありませんが、 'X'は' int& '型です。 'typedef int T;で置き換え、' X'は 'int &&'型になります。右辺値または左辺値のいずれかを 'T &&'にすることに関連する控除を含む魔法はありません。それは参照崩壊ルールです。控除ルールは、ルールがlまたはrの値のいずれかを生成することを可能にするために、特定の方法で「T &&」の「T」が導かれる結果となるが、それは実際の差異である。 – Yakk

+0

私は文言が間違っている場合は私を許してください。正しい言葉の形式は何ですか? –

+0

できる人、投稿する。他人の投稿を批評することができない人。 :)関数の引数や転送参照からのテンプレート型減算について話すために必要な単語の選択は*難*です。 – Yakk

関連する問題