c++ - std::map 模板成员函数指针

标签 c++ c++11

我正在阅读这个 question 的答案,并尝试弄清楚如何在 std::map 中存储模板成员函数指针,c++11。

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;

有什么想法吗?

最佳答案

正如山姆注意到的那样,MFP是一个模板,而 std::map 的第二个模板参数需要一个类型。因此,您需要在使用函数指针填充映射之前获取实际类型。让我为这种情况提出最直接的方法——模板类。有了它,您将需要在对象实例化时列出所有需要的返回类型,但您将能够 Call与任何一种类型。我将使用 std::function s 而不是指针,但您可以轻松回滚到函数指针。

首先,我们不知道类用户需要多少种类型,所以让我们让它可变。自 map需要一个完整的类型,我们需要一堆 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声明是 double 文字,但 double 未在 B<int, float> a; 中列出,我们在 a.Call<double>("whatever") 上得到编译错误.
  • 代码Call方法和callImpl short 的函数根本不会生成,因为我们没有实例化它。

关于c++ - std::map 模板成员函数指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38364357/

相关文章:

c++ - 标准 C++11 是否保证传递给函数的临时对象会在函数结束后被销毁?

c++ - 如何禁用包含文件夹的警告?

c++ - 根据类型特征专门化强制转换运算符

c++ - 类关系 : Architectural Advice Needed

c++ - 线程池怎么了?

c++ - map::emplace 在什么时候创建对象?

c++ - std::shared_ptr、std::weak_ptr 和控制 block

c++ - 如何访问相同特征C++的不同特征专长?

c++ - Visual Studio C++ 2013 调试器不稳定的步骤行为

c++ - 为什么需要 std::move?