【问题标题】:Get a c++11 function wrapper for a member function without using std::bind在不使用 std::bind 的情况下获取成员函数的 c++11 函数包装器
【发布时间】:2014-07-14 21:24:52
【问题描述】:

我正在尝试实现一个基本的信号/插槽系统。一切都已启动并正在运行,但我正在努力提高我的实现的可用性。

目前这是连接信号的方式:

struct X
{
    void memberFunction(int a, int b)
    {
         // do something
    }
};

void globalStaticFunction(int a, int b)
{
    // do something
}

// this is what the signal::connect function looks like at the moment
ConnectionHandle connect(std::function<RetType(Args...)> func);


int main()
{
    // test instance
    X x;

    // example signal
    Signal<void(int, int)> mySignal;


    // connect a static function to the signal
    mySignal.connect(&globalStaticFunction);

    // connect a member function
    // here we have to use std::bind to get a std::function
    mySignal.connect(std::bind(&X::memberFunction, &x, _1, _2));
}

我想为用户提供一种更简单的方法来绑定成员函数。我有这样的想法(类似于 Qt 的做法):

// prefered member function connect
// connect(instance_ptr, function_ptr)
mySignal.connect(&x, &X::memberFunction);

是否可以在不使用 std::bind 的情况下为成员函数获取 std::function 对象?如果没有,是否有一种简单的方法可以只使用成员函数的 instance_ptr 和 function_ptr 生成 std​​::bind 调用?

【问题讨论】:

  • 请参阅stackoverflow.com/questions/21216762/… 了解有关函数部分绑定的一些想法(这基本上是您在这里尝试做的事情)。不幸的是,标准库中没有这样的东西。

标签: c++ c++11


【解决方案1】:

作为Signal的成员:

template<typename C>
ConnectionHandle connect( C* c, RetType(C::*func)(Args...)) {
  return connect( [c,func](Args&&...args) {
    return (c->*func)( std::forward<Args>(args)... );
  });
}

template<typename C, typename Method, typename... Unused>
ConnectionHandle connect( C* c, Method* m, Unused&&...) {
  return connect( [c,func](Args&&...args) {
    return (c->*m)( std::forward<Args>(args)... );
  });
}

第一个重载使您能够区分重载方法。

第二个,如果第一个失败,让你使用签名兼容的方法。

... 部分确保第一个是更好的匹配,如果它们都匹配(我希望!)

Args&amp;&amp;... 应该与std::function 一起使用,假设std::function 正确地完美转发。如果不是,请替换为Args...。

我认为不需要bind 或变体,因为无论如何我们都会立即将其填充到std::function 中。创建一个 lambda,它们非常适合这项任务。

【讨论】:

  • 哦,lambdas。多么简单!
  • @MooingDuck 未绑定成员——值参数! std::function&lt;A(B)&gt; 应该是 std::forward&lt;B&gt;(b) 它的一个参数,因此在其中存储 [](B&amp;&amp;) 应该可以工作。如果B 是一个值,forward 会产生一个右值——如果B 是一个引用,B&amp;&amp; 引用会很好地衰减。
  • 我刚刚开始过渡到 c++11 并且从未真正使用过 boost。我对你可以用 lambdas 做的事情感到震惊,你的解决方案确实非常优雅。我只有一个担心:因此,如果我了解 lambdas 如何正确工作,那么您示例中的 lambda 会创建一个额外的类,该类将 c 和 func 作为构造函数中的参数。 lambda 函数是这个类的成员,然后在 c 指针上调用 func。这是否会以任何方式影响性能?
  • 另外,我阅读了this关于 lambdas 的文章以了解其他内容。作者将静态函数称为“函数”,将成员函数称为“方法”。这是当今引用函数的常用方法吗?来自 java(我的 uni 中的标准)我总是交替使用这两者。
  • 性能更新:我做了一些非常简单的测试,比较了使用 std::bind 和使用 lambda 解决方案绑定的回调的运行时间。我为 Release 和 1Mil 的 Debug 计时了 1 亿次调用。在调试模式下,lambda 解决方案始终快约 20%。但是大多数 STL 东西在调试模式下的性能都很差(我正在使用 VS2013 顺便说一句)。在发布模式下,两种解决方案的性能大致相同(+-1%),这太棒了。
【解决方案2】:

这个技巧在 C++03 中很好,但是对于 C++11,请使用 Yakk 的答案。

类似的东西可能会起作用。

首先:制作一个简单版本的bind,它只绑定this。

template<class T, class RetType, class...Args>
RetType CallMemFn(T* obj, RetType(T::*func)(Args...), Args...args)
{return ((*obj).*(func))(std::forward<Args>(args)...);}

template<class T, class RetType, class...Args>
struct boundthis {
    typedef RetType result_type;
    typedef T* thistype;
    typedef RetType(*signature)(Args...);
    boundthis(T* self, RetType(T::*func)(Args...)) :self(self), func(func) {}
    RetType operator()(Args...args) {return CallMemFn(self,func,std::forward<Args>(args)...);}
private:
    T* self;
    RetType(T::*func)(Args...);
};
template<class T, class RetType, class...Args>
boundthis<T,RetType,Args...> bindthis(T* self, RetType(T::*func)(Args...))
{return boundthis<T,RetType,Args...>(self, func);}

最后,你想要的包装器:

template<class T, class RetType, class...Args>
ConnectionHandle connect(T* obj, RetType(T::*func)(Args...))
{return connect(std::function<RetType(Args...)>(bindthis(obj, func)));}

http://ideone.com/a2rze0

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 2021-04-27
  • 2013-01-08
相关资源
最近更新 更多