2

将具有不同参数的函数指针存储在 void 指针向量中。

unordered_map<string, vector<void*> > List;

template <typename T>
void Listen(string Name, function<void(T)> Function)
{
    List[Name].push_back(&Function);
}

然后我想调用它们,假设它TFire用于Listen.

template <typename T>
void Fire(string Name, T Data)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
    {
        (function<void(T)>)i)(Data);
    }
}

但我得到一个编译器错误,内容为error C2064: term does not evaluate to a function taking 1 arguments in file ...\vc\include\xrefwrap 431 1.

我究竟做错了什么?

4

2 回答 2

2

一方面,您正在获取参数的地址,这里:

List[Name].push_back(&Function);

然后,您尝试将迭代器对象转换为std::function此处的对象:

(function<void(T)>)i)

您尝试做的事情可以这样做,尽管它并不漂亮,但委婉地说:

unordered_map<string, vector<void*> > List;

template <typename T>
void Listen(string Name, function<void(T)> &Function)
{
    List[Name].push_back(&Function);
}

template <typename T>
void Fire(string Name, T Data)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
    {
      function<void(T)> *ptr = *i;

      (*ptr) (Data);
    }
}

它可能会以多种方式中断,例如,您无法控制以某个名称注册的函数 inListen是否使用正确的参数 inFire调用 - 考虑调用Listen<int> ("foo", f);然后执行Fire<double> ("foo", 3.14);

另一种方法 - 只需为回调传递闭包:

unordered_map<string, std::vector<function<void()> > > List;

void Listen(string Name, function<void()> Function)
{
    List[Name].push_back(Function);
}

void Fire(string Name)
{
    auto Functions = List[Name];

    for (auto i = Functions.begin(); i != Functions.end(); ++i)
      (*i) ();
}
于 2012-12-01T20:45:05.737 回答
1
#include <functional>
#include <unordered_map>
#include <memory>
#include <string>
#include <vector>

template<typename T> struct BlockDeduction{typedef T type;};
struct BaseCallback {
  virtual ~BaseCallback();
  template<typename T>
  void DoCall( typename BlockDeduction<T>::type&& t ) const;
};
template<typename T>
struct Callback: BaseCallback
{
  std::function<void(T)> func;
  Callback( std::function<void(T)> const& f ):func(f) {}
};


template<typename T>
void BaseCallback::DoCall( typename BlockDeduction<T>::type&& t ) const {
  Assert( dynamic_cast<Callback<T>const*>(this) );
  static_cast<Callback<T>const*>(this).func(std::forward(t));
}

typedef std::unique_ptr<BaseCallback> upCallback;
template<typename T>
upCallback make_callback( std::function<void(T)> const& f ) {
  return upCallback( new Callback<T>( f ) );
}


struct Listener {
  std::unordered_map< std::string, std::vector<upCallback>> List;
  template<typename T>
  void Listen( std::string Name, std::function<void(T)> f) {
    List[Name].push_back( make_callback(f) );
  }
  template<typename T>
  void Fire( std::string Name, typename BlockDeduction<T>::type&& t ) {
    auto callbacks = List.find(Name);
    if (callbacks == List.end()) return;
    for(auto it = callbacks->second.begin(); it != callbacks->second.end(); ++it) {
      if (it +1 = callbacks->second.end())
      {
        (**it).DoCall<T>( std::forward(t) );
      } else {
        (**it).DoCall<T>( t );
      }
    }
  }
};

... 或类似的东西。

这会在地图中存储一个副本,std::function通常是包裹起来的。内存是通过unique_ptr. 我小心地在类型必须与您安装时使用的类型完全相同的点处阻止类型推导Listener(此时的自动类型推导相当脆弱)。

在调试中,如果您违反 Name<->type 映射,您将获得断言失败。

需要为 nullary 回调做一些额外的工作。只需编写一个DoCall转换BaseCallback为 的Callback<void>,专门化为Callback<void>空值function包装器,专门化为make_callback空值function,然后编写一个调用 bare的Fire(string)方法。ListenerDoCall

或者创建一个struct Empty并使用 lambdas 将 nullary 函数包装在 中function<void(Empty)>,这将涉及更少的代码,但在运行时会更慢。

于 2012-12-01T21:30:38.280 回答