私は、あなたが直面しているユースケースを理解していないことを少し誤解しています。
まず第一に、これは関数テンプレートです:
struct A
{
template <typename... Args>
void f(Args... args)
{
}
};
そして、これは関数テンプレートではありません:(関数テンプレート付き)旧定義では
template <typename... Args>
struct A
{
void f(Args... args)
{
}
};
引数の型推論起こる。後者の場合、タイプ控除はありません。
機能テンプレートは使用していません。クラステンプレートから非テンプレートメンバー関数を使用しています。この特定のメンバー関数については、その署名は修正されています。
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args);
};
とそのメンバ関数を参照する以下のような:
&trap<decltype(&Base::target), &Base::target>::call;
あなたは、静的な非テンプレートcall
関数へのポインタ付きで終わる以下のようなあなたのtrap
クラスを定義することで
固定の署名で、target
関数の署名と同じです。
今、そのcall
関数は、中間呼び出し側として機能します。あなたはcall
関数を呼び出すされ、その機能はtarget
のパラメータを初期化するために、独自の引数を渡す、target
メンバ関数を呼び出します、と言う:
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(Args... args)
{
return (get_base()->*t)(args...);
}
};
はtrap
クラステンプレートをインスタンス化するために使用target
関数であると仮定します定義された次のように
struct Base
{
int target(Noisy& a, Noisy b);
};
を使用すると、以下のcall
機能で終わるtrap
クラスをインスタンス化することによって:
幸いにも10
// what the compiler *sees*
static int call(Noisy& a, Noisy b)
{
return get_base()->target(a, b);
}
a
を参照でを渡され、それは単にtarget
のパラメータで参照の同じ種類によって転送され、結合されます。残念ながら、これはb
オブジェクトのために保持していない - に関係なくNoisy
クラスはそのいずれかが値でを渡されるので、あなたは、b
インスタンスの複数のコピーを作っている、移動可能であるかどうか:
DEMO 1
これはやや非効率的である:あなたがにはxValueをb
インスタンスを回すことができる場合にのみ、移動、コンストラクタ呼び出しにそれを回す、少なくとも1つのコピー・コンストラクタ呼び出しを保存している可能性が :
これで、2番目のパラメータの代わりに移動コンストラクタが呼び出されます。
これまでのところよくできていましたが、手動で行われました(std::move
は、移動セマンティクスを適用するのが安全だと知りました)。さて、質問はパラメータパック上で動作しているときと同じ機能が適用され得るか、次のとおりです。?
return get_base()->target(std::move(args)...); // WRONG!
あなたはstd::move
それぞれの呼び出しやパラメータパック内のすべての引数を適用することはできません。これはおそらくすべての引数に等しく適用されるとコンパイラエラーを引き起こすでしょう。幸い
DEMO 2
、Args...
は転送参照ないにもかかわらず、std::forward
ヘルパー関数が代わりに使用することができます。左辺値参照の
を(例えばT
がNoisy&
の場合)::それはstd::forward
は異なる動作をします(左辺値参照または非左辺値参照)<T>
タイプがstd::forward<T>
であるものに応じて、ある値式のカテゴリは左辺値のままです(つまり、Noisy&
)。非左辺値、参照用
(T
がNoisy&&
またはプレーンNoisy
であれば例えば)式の値カテゴリはxValue(すなわち、Noisy&&
)となります。
static R call(Args... args)
{
return (get_base()->*t)(std::forward<Args>(args)...);
}
あなたがで終わる:はxValueにb
を含む式の値カテゴリを回す
static int call(Noisy& a, Noisy b)
{
// what the compiler *sees*
return get_base()->target(std::forward<Noisy&>(a), std::forward<Noisy>(b));
}
以下のようなtarget
機能を定義することによって、それは言って
b
、これはNoisy&&
です。これにより、コンパイラはa
のままにして、target
関数の2番目のパラメータを初期化するために移動コンストラクタを選択できます。
DEMO 3(DEMO 1と出力を比較)
基本的に、これはstd::forward
のためにあるものです。通常、std::forward
は転送参照と使用され、T
は転送参照のタイプ控除の規則に従って推測されるタイプを保持します。その型に応じて(引数の値のカテゴリに依存しないで)異なる動作を適用するので、常に<T>
部分を明示的に渡す必要があることに注意してください。明示的な型テンプレートの引数<T>
がなければ、std::forward
は、(パラメータパックを展開するときのように)名前によって参照される引数の左辺参照を常に推測します。
さらに、は、あるタイプから別のタイプへの引数の一部を変換し、他のタイプはすべて転送します。あなたは、パラメータパックからstd::forward
ING引数を持つトリックを気にしない、それは常にコピーコンストラクタを呼び出すために罰金だ場合は、お使いのバージョンはOKです:しかし
template <typename T> // transparent function
T&& process(T&& t) {
return std::forward<T>(t);
}
Bar process(Foo x) { // overload for specific type of arguments
return Bar{x};
}
//...
get_base()->target(process(args)...);
DEMO 4
デモでそのNoisy
引数のコピーを避けたい場合、あなたはstd::forward
が適切な行動(トンを適用することができるように何とか、Args
種類以上process
コール、とパスでstd::forward
コールを結合する必要がありますX値に変換するか何もしない)。これを実装する方法の簡単な例を説明しました。
template <typename T, typename U>
T&& process(U&& u) {
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Bar process(Foo x) {
return Bar{x};
}
//...
get_base()->target(process<Args>(args)...);
これは単なる1つのオプションです。それは、単純化し、書き換え、または並べ替え、あなたがprocess
機能(バージョン)を呼び出す前に、std::forward
が呼び出されるようにすることができます。
get_base()->target(process(std::forward<Args>(args))...);
DEMO 5(DEMO 4との出力を比較)
そして、それあなたのバージョンでもうまく動作します。つまり、追加のstd::forward
はコードを少し最適化するだけで、provided ideaはその機能の実装の1つに過ぎません(これは同じ効果をもたらします)。
これらの例では、プロセス関数が呼び出されるコンテキストがありません - 常に明示的な型テンプレート引数を使用します(少なくとも非左辺参照をxvaluesに変換するという考え方でした)。あなたはあなたのバージョンを手放すことができますが、ターゲット関数のパラメータを初期化している間は常にcopy ctorを呼び出すでしょう –
バージョン1は何らかの形で「汚い」ということに同意します。なぜなら、テンプレート化されていない関数のオーバーロード戻り値の型。しかし、バージョン2では何の意味も感じられません。コンパイラは、使用状況でそれを推論することができないため、常に最初のテンプレートパラメータを指定する必要があります。 –
@PiotrS。、私は文脈を提供するために編集しました(私の実際のユースケースにマッチすると思います)。これらの最後のカップルのあなたの貴重な助けのおかげで、私は現在実用的なソリューションを持っています([こちら](http://stackoverflow.com/q/27866483/435129))。これは私が理解していないコードを組み込むのが好きではないので、私が噛んでいる最後のコンポーネントです。 –