c++ - 使用 std::function 的回调替代方案

标签 c++ c++11 callback std-function stdbind

目前我正在尝试基本上执行以下操作的代码:

void f(int x) { cout << "f("<<x<<")" << endl; }

class C
{
public:
   void m(int x) { cout << "C::m("<<x<<")" << endl; }
};

class C2
{
public:
   void registerCallback(function<void(int)> f)
   {
      v.push_back(f);
   }

private:
   vector<function<void(int)>> v;

   void callThem()
   {
      for (int i=0; i<v.size(); i++)
      {
         v[i](i);
      }
   }
};

int main()
{
   C2 registrar;

   C c;
   registrar.registerCallback(&f); // Static function
   registrar.registerCallback(bind(&C::m, &c, placeholders::_1)); // Method

   return 0;
}

这很好用。但是我陷入了这种模式。我想检查回调是否已经注册,并且我希望能够通过从 vector 中删除回调来注销回调。我刚刚了解到 std::function 对象无法进行比较,这意味着无法在容器中搜索它们的存在。

所以我需要一个替代方案。当然,我希望保留编译时类型检查和注册任意类方法的能力。

如何实现类似的解决方案,允许注销回调并检查双重注册?我需要接受任何权衡吗?

最佳答案

潜在的问题是大多数函数对象是不可比较的。虽然普通函数指针和带有相等运算符的用户定义函数对象可以进行比较,但 lambda 表达式是 std::bind() 的结果。等不能。使用函数对象的地址来识别它们通常不是一种合适的方法,因为对象往往会被复制。 可能可以使用 std::reference_wrapper<Fun>为了避免复制它们,但对象存储在 std::function<F> 中仍然会有不同的地址。

使用 C++11 可变参数模板,创建 std::function<...> 的自定义版本相当容易。它提供比较设施。甚至可能有两个版本:

  • 一个版本接受任意函数对象,但显然只能比较可比较的函数对象:根据构造函数参数是否提供相等运算符,使用合适的基类。
  • 一个始终提供有效比较的版本,显然不能用于非相等可比较对象。

后者更容易定义,看起来像这样:

template <typename...> class comparable_function;
template <typename RC, typename... Args>
class comparable_function<RC(Args...)> {
    struct base {
        virtual ~base() {}
        virtual RC    call(Args... args) = 0;
        virtual base* clone() const = 0;
        virtual bool  compare(base const*) const = 0;
    };
    template <typename Fun>
    struct concrete: base {
        Fun fun;
        concrete(Fun const& fun): fun(fun) {}
        RC call(Args... args) { return this->fun(args...); }
        base* clone() const { return new concrete<Fun>(this->fun); }
        bool compare(base const* other) const {
             concrete const* o = dynamic_cast<concrete<Fun>>(other);
             return o && this->fun == o->fun;
        }
    };
    std::unique_ptr<base> fun;
public:
    template <typename Fun>
    comparable_function(Fun fun): fun(new concrete<Fun>(fun)) {}
    comparable_function(comparable_function const& other): fun(other.fun->clone()) {}
    RC operator()(Args... args) { return this->fun->call(args); }
    bool operator== (comparable_function const& other) const {
        return this->fun->compare(other.fun.get());
    }
    bool operator!= (comparable_function const& other) { return !(this == other); }
};

我想,我忘记了(和/或打错了)一些东西,但这正是我所需要的。对于可选的可比较版本,您将有两个版本的 concrete :一个如上实现,另一个总是返回 false .取决于是否有运算符==对于 Fun在构造函数中,您将创建一个或另一个。

关于c++ - 使用 std::function 的回调替代方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27627380/

相关文章:

C++11 - 将非静态数据成员声明为 'auto'

Android Facebook SDK AppInviteDialog 回调不工作

javascript - 代码不等待回调函数完成

c++ - 数组中的最后一个变量不正确

c++ - 将 struct tm 转换为 time_t

c++ - 我是否应该包含 <vector>,即使它已经包含在 <regex> 中?

c++11 - std::unique 定义了删除哪个元素?

c++ - std::copy 用于多维数组

c++ - 自定义重载运算符 << 与 log4cxx

javascript - 将 javascript 回调传递给使用 deployJava 部署的 java applet