2016-07-14 100 views
2

我正在閱讀這個question的答案,並試圖找出如何在模板成員函數指針C++ 11中存儲std::mapstd ::模板成員函數指針的映射

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)(); 
    } 
}; 

的類型別名編譯,但是當我用它在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; 

任何想法?

+0

您必須指定模板參數,如'std :: map > fmap;'。否則''A :: fmap'可能會在剛剛寫入'A a;'時被實例化。 – songyuanyao

回答

1

std::map的第二個模板參數是地圖值的類或類型。例如:

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

第二個模板參數是類型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

正如Sam注意到的,MFP是一個模板,而std::map的第二個模板參數需要一個類型。因此,在使用函數指針填充地圖之前,您需要獲取實際的類型。讓我提出這個案例最直接的方法 - 模板類。有了它,你將需要在對象實例化中列出所有需要的返回類型,但是你將能夠使用任何類型的Call。我將使用std::function而不是指針,但可以輕鬆回滾到函數指針。

首先,我們不知道類用戶需要多少類型,所以讓我們將其作爲可變參數。由於map需要一個完整的類型,我們需要一堆地圖 - 每種類型一個。獲得它的最常見方法是一個元組,在我們的例子中需要擴展包。通過元組,我們可以在編譯時搜索需要的映射,然後在運行時按名稱搜索函數。看看代碼與解釋:

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; 
} 

一些注意事項:

  • 2.5f聲明是雙重的文字,但雙不列在B<int, float> a;中,我們在上得到編譯錯誤。
  • 代碼Call方法和callImpl函數short根本不會生成,因爲我們沒有實例化它。