如何绑定传递未指定的调用包装器的成员函数模板

时间:2013-02-27 15:07:30

标签: c++ c++11 stdbind

我尝试使用VC11和g ++ 4.7.2编译以下示例:

#include <functional>

class X {
public:
  template <typename T>
  explicit X(T t)
  {
    std::bind(&X::invoke<T>, this, t)();
  }
private:
  template <typename T>
  void invoke(T t)
  {
    t();
  }
};

class Y {
  public:
    void foo() {
      //...
    }
};


int main() {
  Y y;
  X x(std::bind(&Y::foo, &y));
  return 0;
}

但它完成了错误。我不确定粘贴整个编译器输出是否合理,但通常是

vc11说:

  

错误C2664:'void std :: _ Pmf_wrap :: operator()(_ Farg0&amp;,_ V0_t)const':无法将参数3从'void'转换为'std :: _ Bind,Y *,std :: _ Nil,的std :: _无,性病:: _无,性病:: _无,性病:: _无,性病:: _无&GT;” c:\ program files(x86)\ microsoft visual studio 11.0 \ vc \ include \ functional 1152 1 ConsoleApplication1(Microsoft Visual C ++编译器2012年11月CTP)

和g ++:

  

编译完成但有错误:
  source.cpp:实例化'X :: X(T)[带T = std :: _ Bind(Y *)&gt;]':
  source.cpp:28:33:从这里要求
  source.cpp:8:9:错误:无法调用'(std :: _ Bind_helper(Y *)&gt;),X * const,std :: _ Bind(Y *)&gt;&amp;&gt; :: type {aka std :: _ Bind(Y *)&gt;)&gt;(X *,std :: _ Bind(Y *)&gt;)&gt;})()'

有没有办法解决这个问题。保存主要思想对于我来说非常重要 - 可以使用任何可调用对象(函数对象,函数指针或std::bind()函数返回的调用包装器)实例化的类。

如果有人帮忙,我将不胜感激。

P.S。如果我创建X的实例,它会传递函数对象或函数指针。

2 个答案:

答案 0 :(得分:4)

我认为他们在boost::bind中使用std::bind时忽略了boost::protect()的重要部分,即#include <boost/bind/protect.hpp> // ... X x(boost::protect(std::bind(&Y::foo, &y))); 。您的代码可以修改如下:

template <typename T>
explicit X(T t)
{
    auto tt = boost::protect(t);
    auto f = std::bind(&X::invoke<decltype(tt)>, this, tt);
    f();
}

或者,或者:

template <typename T>
explicit X(T t)
{
    auto f = [t, this]() { this->invoke(t); };
    f();
}
// ...

X x([&y](){ y.foo(); });

请参阅http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html

  

虽然默认情况下第一个参数未被评估,但所有其他参数都是。有时,有必要不评估第一个之后的参数,即使它们是嵌套的绑定子表达式。这可以通过另一个掩盖类型的函数对象protect来实现,这样bind就无法识别和评估它。调用时,protect会简单地将参数列表转发给未修改的其他函数对象。

     

标头boost / bind / protect.hpp包含一个protect实现。要保护绑定函数对象不受评估,请使用protect(bind(f,...))。


Scott Meyers即将发布的Effective C++11: Content and Status将推荐首选lambdas到std :: bind 。在C ++ 11中,您可以简单地执行:

{{1}}

答案 1 :(得分:2)

问题的根本原因似乎是std::bind执行其参数的内部复制,特别是t

您可能希望以这种方式解决方法:

  template <typename T>
  explicit X(T t)
  {
      std::bind(&X::invoke<T>, this, std::placeholders::_1)(t);
      //                             ^^^^^^^^^^^^^^^^^^^^^  ^
  }

这也可行,但你不会让bind的结果比论据t更长(否则,你会传递一个悬挂的引用{{1} }):

invoke<T>()

<强>更新

上述解决方案是有助于实现您在示例中显示的内容的解决方法。但是,从评论中可以看出,您的用例可能大不相同(例如,传递 template <typename T> explicit X(T t) { std::bind(&X::invoke<T>, this, cref(t))(); // ^^^^^^^ } 的结果以供以后评估)。

正如Maxim Yegorushkin在他的回答中正确指出的那样,概念上正确的解决方案在于使用诸如Boost&#39; bind之类的构造。

如果您不想使用Boost,使用C ++ 11的可变参数模板定义您自己的protect函数非常容易:

protect()

最终,这就是你在课堂上使用它的方式,正如Maxim建议的那样:

// Imitates boost::protect, but with variadic templates and perfect forwarding
namespace detail
{
    template<typename F>
    struct protect
    {
    private:

        F _f;

    public:

        explicit protect(F f): _f(f)
        {
        }

        template<typename... Ts>
        auto operator () (Ts&&... args) -> 
            decltype(_f(std::forward<Ts>(args)...))
        {
            return _f(std::forward<Ts>(args)...);
        }
    };
}

template<typename F>
detail::protect<F> protect(F&& f)
{
    return detail::protect<F>(std::forward<F>(f));
}