【问题标题】:bind binded function as argument绑定绑定函数作为参数
【发布时间】:2014-12-30 01:19:55
【问题描述】:

我有一个类foo 有一个方法bar,它采用一些可调用的东西(函数指针/函子)。这个可调用的东西应该被传递给另一个方法doit作为绑定元素与第三个方法bar_cb方法。

#include <functional>
#include <iostream>

class foo {
public:
    template<typename T>
    void bar(T&& t) {
        std::cout << "bar\n";
        doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
    }

    template<typename T>
    void doit(T&& t) {
        std::cout << "doit\n";
        t();
    }

    template<typename T>
    void bar_cb(T&& t) {
        std::cout << "bar_cb\n";
        t();
    }
};


void lala() {
    std::cout << "lala\n";
}

class functor {
public:
    void operator()() {
        std::cout << "functor::operator()\n";
    }
};


int main() {
    foo f;
    functor fn;
    f.bar(fn);
    f.bar(std::bind(lala));  // error

    return 0;
}

这适用于functors,但不适用于作为foo::bar 参数的绑定函数(在我的示例中为lala)。是否可以将一个不可知的类型传递给一个方法并在这个方法中将它作为参数绑定到另一个(如果可以的话)?

我知道我可以在函数周围包装一个仿函数(例如std::function),但由于我可以调用一个不可知的类型,我认为还有一种方法可以绑定它(我想我只是缺少一些简单的东西)。

Here 示例链接。

【问题讨论】:

  • 您要解决的实际(更高级别)问题是什么?
  • @JohnZwinck 问题是我想要一个类,它接受一个仿函数或一个函数,并用这个 callable 作为参数调用其他仿函数。真正的代码是一个可变参数模板类,具有不同的仿函数作为模板参数。但是,我试图将问题总结为一个简单的非模板类,以了解问题所在。我也想避免std::function 的开销。
  • 您遇到了bind 对绑定表达式的特殊处理。 stackoverflow.com/questions/10777421/stdbind-a-bound-function
  • 使用 lambda 代替 std::bind,也许吧? Live example。不过,不确定对完美转发的影响。
  • 写一个unbind 接受一个绑定表达式并将它包装在一个不是绑定表达式的类型中?但在其他方面表现相同?

标签: c++ c++11 bind function-pointers boost-bind


【解决方案1】:

主要问题是您的bar_cb(T&amp;&amp;) 没有推导出模板参数,因为模板参数实际上是在使用&amp;foo::template bar_cb&lt;X&gt; 和一些模板参数X 时指定的。但是,bind() 表达式将复制绑定函数,即它可能具有也可能不具有将被推导的类型。此外,std::bind()不会传递bind()-expression,而是调用它们!

最简单的解决方法是使用std::bind()来绑定函数,而是使用lambda函数:

template<typename T>
void bar(T&& t) {
    std::cout << "bar\n";
    doit([=](){ this->bar_cb(t); });
}

这样做让编译器推断出bar_cb() 的更正参数类型(对于C++14,您可能希望使用捕获[this,t = std::forward&lt;T&gt;(t)],尽管您的bar_cb() 仍然看不到右值)。

要通过另一个bind()-表达式传递一个已经bind()-表达式,而不是bind(),考虑内部bind()-表达式一个bind()-表达式,你需要让它看起来好像不是bind()-表达式。你可以用一个瘦函数包装器来做到这一点:

template <typename Fun>
class unbinder {
    Fun fun;
public:
    template <typename F>
    unbinder(F&& fun): fun(std::forward<F>(fun)) {}
    template <typename... Args>
    auto operator()(Args&&... args) const
        -> decltype(fun(std::forward<Args>(args)...)) {
        return fun(std::forward<Args>(args)...);
    }
};
template <typename Fun>
auto unbind(Fun&& fun)
    -> unbinder<Fun> {
    return unbinder<Fun>(std::forward<Fun>(fun));
}

由于bind() 表达式中存储的函数将通过左值传递,因此您需要为您的bar_cb() 声明一个不同的声明,但是:

template<typename T>
void bar_cb(T& t) {
    ...
}

这样,您可以使用

注册bind()-表达式
f.bar(unbind(std::bind(lala)));

如果你想使用f.bar(std::bind(lala)),你需要一个bar()的条件定义:如果它接收到bind()-表达式,它需要通过应用自动隐藏它是bind()-表达式的事实unbind() 或类似的东西:

template<typename T>
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
    std::cout << "bar (non-bind)\n";
    doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
}
template<typename T>
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
    std::cout << "bar (bind)\n";
    doit(std::bind(&foo::template bar_cb<unbinder<T>>, this, unbind(std::forward<T>(t))));
}

【讨论】:

  • 感谢您的详细回答。我会尝试两者。顺便说一句,您在 lambda 函数中按值传递是否有特定原因。
  • 按值捕获绑定参数更类似于使用srd::bind() 的原始构造(如果您想通过引用wirh std::bind() 捕获值,您将使用std::ref())。此外,它似乎是更防故障的起点,因为没有生命周期问题。最后,按值捕获通用参数使函数的用户可以选择覆盖默认值,也可以使用std::ref()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-30
  • 1970-01-01
  • 1970-01-01
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-01-22
相关资源
最近更新 更多