2016-12-07 11 views
2

テンプレートファンクタを作成しようとしていますが、これは引数オブジェクトとメンバ関数として任意の数のパラメータを使用します。私はテンプレートを使ってコードを正しく書く方法を理解できません。任意のパラメータを持つテンプレートファンクタ

template<typename ItemT, 
    class T, 
    typename ...Args> 
struct Builder 
{ 
    ItemT operator()(T& object, ItemT (T::*method)(Args...), Args && ... args) 
    { 
     return (object.*method)(std::forward<Args>(args)...); 
    } 
}; 

struct Object 
{ 
    int method(int, int, int) { return 4; } 
}; 


int main() 
{ 
    Object obj;  
    Builder<int, Object>()(obj, &Object::method); // Error here 
} 

パラメータを指定せずにObject ::メソッドを作成するとコードがコンパイルされます。しかし、パラメータで - いいえ。

重大度コード説明プロジェクトファイルの行抑制状態 エラーC2664 'int型のビルダー::演算子()(T &、ItemT(オブジェクト__thiscall :: *)(無効))':int型から引数2を変換することはできません(__thiscall Object :: *)(int、int、int) 'から' int(__thiscall Object :: *)(void) 'の下書きc:\ drafts \ main.cpp 139

+3

あなたが 'Builder'オブジェクトを宣言したとき、' typename ... Args'は何を定義するために渡すのを忘れましたか? – NathanOliver

+0

あなたは 'Builder 'を必要とし、 'Object :: method'が引数として望む3つのintを実際に渡す必要があります。より一般的には、練習のポイントが何であるかは不明です。あなたは本当に解決しようとしている問題は何ですか? –

+0

ネイサンが言ったこと。これはまた、標準ライブラリがオブジェクトを構築するだけのさまざまな "make_ *"フリー関数を提供する理由です。したがって、テンプレート引数の控除が発生する可能性があります。 C++ 1zでは、コンストラクタはクラスのテンプレートパラメータを推定し、コードを有効にすることができます。 – StoryTeller

答えて

4

Builderの現在の定義は、これはあなたがそれをインスタンス化する必要があるかです:

Builder<int, Object, int, int, int>()(obj, &Object::method, 0, 0, 0); 
// ^ ^ ^^^^^^^^^^^^^       ^^^^^^^ 
//  ItemT | |          | 
//    T Args...        args... 

args...パラメータ展開コールはBuilderに渡されたTArgs...パックに一致する必要があります。

wandbox example


ここ代替以下厳密デザインのこの場合

int main() 
{ 
    Object obj;  
    Builder<Object>()(obj, &Object::method, 0, 0, 0); 
} 

の種類:上記Builderこのように使用することができる

template<typename T> 
struct Builder 
{ 
    template <typename TFnPtr, typename... Args> 
    auto operator()(T& object, TFnPtr method, Args && ... args) 
    { 
     return (object.*method)(std::forward<Args>(args)...); 
    } 
}; 

メンバ関数ポインタは、TFnPtrによって導かれ、特定のパラメータセットに制約されません。可変パラメータはもうBuilderの一部ではなく、Builder::operator()の一部なので、推測して(object.*method)に転送することができます。

wandbox example

1

あなたは完全にBuilderにテンプレートを避け、単にテンプレート引数控除に頼ることができます。

struct Builder { 
    template <typename Obj, typename R, typename ... FArgs, typename ... Args> 
    R operator()(Obj& obj, R (Obj::*fn)(FArgs...), Args&&... args) { 
     return (obj.*fn)(std::forward<Args>(args)...); 
    } 
}; 

私は完璧な転送を可能にするために2つのパラメータパックを使用することを選択した:operator()コールの値カテゴリ必ずしも対象となる方法と一致するとは限りません。これはまた、メンバ関数ポインタを適用するときに暗黙の引数の変換を可能にします。この実装は、constメソッドがObjと一致しないことに注意してください。

auto戻り値の型(C++ 14)または後続の戻り値の型(C++ 11)を使用して、少しでもリラックスすることができます。 Vittorioの答えは既にC++ 14の方法を示しているので、私は後者に取り組んでいます。その後、operator()は次のようになります。

template <typename Obj, typename FnPtr, typename ... Args> 
auto operator()(Obj& obj, FnPtr fn, Args&&... args) 
    -> decltype((obj.*fn)(std::forward<Args>(args)...)) { 
    return (obj.*fn)(std::forward<Args>(args)...); 
} 

その後、使い方は単純に次のようになります。Coliru上

Object obj; 
Builder()(obj, &Object::method, 0, 0, 0); 

live demo

関連する問題