2016-07-14 23 views
2

このquestionの答えを読んで、std::mapにテンプレートメンバー関数ポインタ、C++ 11を格納する方法を考えてみました。テンプレートメンバ関数ポインタのstd :: map

error: type/value mismatch at argument 2 in template parameter list for ‘template<class _Key, class _Tp, class _Compare, class _Alloc> class std::map’ 
std::map<std::string, MFP> fmap; 

任意のアイデア:私は次のエラーを取得するstd::map宣言でそれを使用する場合

class A { 
    template<typename T> 
    using MFP = T (A::*)(); 

    std::map <string, MFP> fmap; 

    template<typename T> 
    T f() { return 1; } 

    template<typename T> 
    T g() { return 1.0f; } 

    A() { 
    fmap.insert(std::make_pair("f", &A::f)); 
    fmap.insert(std::make_pair("g", &A::g)); 
    } 

    template<typename T> 
    T Call(const string & s) { 
    MFP fp = fmap[s]; 
    return (this->*fp)(); 
    } 
}; 

タイプの別名はコンパイルけど?

+0

'std :: map > fmap;'のようなテンプレート引数を指定する必要があります。それ以外の場合、 'A :: fmap'は' A a; 'と書くだけでインスタンス化できます。 – songyuanyao

答えて

1

std::mapの2番目のテンプレートパラメータは、マップの値のクラスまたはタイプです。例:

std::map<std::string, int> mapsi; 

第2のテンプレートパラメータは、タイプintで整数です。

あなたの宣言:

std::map <string, MFP> fmap; 

MFPは、クラス、またはタイプではありません。 MFPは別のテンプレートです。これは次の文章と同じです。

std::map <string, template<typename T> T (A::*)()> fmap; 

これは意味がありません。したがって、コンパイルエラーです。

あなたは書くことができます:

std::map <string, MFP<int>> fmap; 

または

std::map <string, MFP<std::string>> fmap; 

MVP<int>

MFP<std::string>が実際の種類、実際のクラスである、したがって、あなたがマップにそれらを置くことができます。テンプレートは実際のクラスやタイプではないので、テンプレートをマップに配置することはできません。

template<typename T> 
T f() { return 1; } 

template<typename T> 
T g() { return 1.0f; } 

これはメンバー機能であると思われます。彼らはそうではありません。彼らはメンバーテンプレートです。メンバ関数を指すポインタだけ、メンバテンプレートへのポインタなどはありません。

1

サムが気付いたように、MFPはテンプレートですが、2番目のテンプレート引数std::mapには型が必要です。したがって、マップに関数ポインタを設定する前に、実際の型を取得する必要があります。このケーステンプレートクラスの最も簡単なアプローチを提案しましょう。これを使用すると、オブジェクトのインスタンス化に必要なすべての戻り値の型をリストする必要がありますが、その型のいずれでもCallにできるようになります。ポインタの代わりにstd::functionを使用しますが、簡単に関数ポインタにロールバックできます。

最初に、クラスのユーザーが必要とするタイプの数がわからないので、それをバリデーションしてみましょう。 mapには完全なタイプが必要なので、タイプごとに1つのマップが必要です。それを得る最も一般的な方法は、私たちのケースではパックの拡張が必要な​​タプルです。タプルを使用すると、コンパイル時に必要なマップを検索し、実行時にそのマップ内の関数を名前で検索できます。説明してコードを見てみましょう:

template<typename ...Types> 
class B { 
private: 

    // Template alias for std::function. 
    template<typename T> 
    using MFP = std::function<T()>; 

    /* Tuple of maps from std::string to MFP for all types 
    in Types parameter pack. */ 
    std::tuple<std::map<std::string, MFP<Types>>...> fmap; 

    template<typename T> 
    T f() { return 2.5; } 

    template<typename T> 
    T g() { return 1.0f; } 

    // Call implementation with compile-time pattern matching. 
    // T is return type, U is current matching type 
    template<typename T, size_t idx, typename U, typename ...Ts> 
    struct CallImpl { 
    static T callImpl(B* this_ptr, const std::string & s) { 
     /* If we exhausted Ts pack, we have no proper instance for 
     requested return type. Let's print a human-readable 
     compilation error message. */ 
     static_assert((sizeof ... (Ts)) > 0, 
     "Requested return type not found."); 
     /* Otherwise discard U, increment tuple index 
     and try the next type. */ 
     return CallImpl<T, idx + 1, Ts...>::callImpl(this_ptr, s); 
    } 
    }; 

    /* This partial specialization is called when return 
    * type (T in above declaration) matches 
    * stored type (U in above declaration). */ 
    template<typename T, size_t idx, typename ...Ts> 
    struct CallImpl<T, idx, T, Ts...> { 
    static T callImpl(B* this_ptr, const std::string & s) { 
     /* First, get the map from tuple by index. 
     This operation is either always valid in runtime or does not compile. 
     Next, get function object from map. It may fail in runtime 
     if user passed invalid string, so consider using map::at 
     or add any other sensible logic for this case. */ 
     return std::get<idx>(this_ptr->fmap)[s](); 
    } 
    }; 

public: 

    B() { 
    /* Populate map with objects. Ellipsis in the last line 
     expands Types as needed. */ 
    fmap = std::make_tuple(std::map<std::string, MFP<Types>>{ 
     {"f", std::bind(std::mem_fn(&B::f<Types>), this)}, 
     {"g", std::bind(std::mem_fn(&B::g<Types>), this)} 
    }...); 
    } 

    template<typename T> 
    T Call(const std::string & s) { 
    /* Start pattern matching with zero index. */ 
    return CallImpl<T, 0, Types...>::callImpl(this, s); 
    } 
}; 

使用法:

int main() { 

    B<int, float, short> a; // Provides int, float and short return types. 
    std::cout << a.Call<int>("f") << std::endl; // Prints 2, which is 2.5 casted to int. 
    std::cout << a.Call<float>("f") << std::endl; // Prints 2.5 

    // Compilation error with "Requested type not found." message among others. 
    std::cout << a.Call<double>("f") << std::endl; 
} 

いくつかの注意:f宣言で

  • 2.5はリテラル、二重ですが、ダブルではありませんB<int, float> a;に記載されていますが、これはでコンパイルエラーが発生します。
  • コードCallcallImplshortの機能はインスタンス化されていないため、まったく生成されません。
関連する問題