2016-07-14 48 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根本不会生成,因为我们没有实例化它。