Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I bind a member function template passing unspecified call wrapper

Tags:

c++

c++11

stdbind

I tried to compile the following example using VC11 and 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;
}

but it finished with errors. I'm not sure if it is reasonable to paste the whole compilers output but generally

vc11 says:

error C2664: 'void std::_Pmf_wrap::operator ()(_Farg0 &,_V0_t) const' : cannot convert parameter 3 from 'void' to 'std::_Bind,Y *,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>' c:\program files (x86)\microsoft visual studio 11.0\vc\include\functional 1152 1 ConsoleApplication1 (Microsoft Visual C++ Compiler Nov 2012 CTP)

and g++:

Compilation finished with errors:
source.cpp: In instantiation of 'X::X(T) [with T = std::_Bind(Y*)>]':
source.cpp:28:33: required from here
source.cpp:8:9: error: no match for call to '(std::_Bind_helper(Y*)>), X* const, std::_Bind(Y*)>&>::type {aka std::_Bind(Y*)>)>(X*, std::_Bind(Y*)>)>}) ()'

Is there a way to solve this issue. It's very important for me, to save the main idea - a class that can be instantiated with any callable object (function object, function pointer or call wrapper returned by std::bind() function).

I would be grateful if someone help.

P.S. It compiles if I create an instance of X, passing function object or function pointer.

like image 527
orlin Avatar asked Feb 27 '13 15:02

orlin


1 Answers

I think they omitted an important bit of boost::bind when adopting it into std::bind, namely boost::protect(). Your code can be fixed like the following:

#include <boost/bind/protect.hpp>
// ...
X x(boost::protect(std::bind(&Y::foo, &y)));

Or, alternatively:

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

See http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html

Although the first argument is, by default, not evaluated, all other arguments are. Sometimes it is necessary not to evaluate arguments subsequent to the first, even when they are nested bind subexpressions. This can be achieved with the help of another function object, protect, that masks the type so that bind does not recognize and evaluate it. When called, protect simply forwards the argument list to the other function object unmodified.

The header boost/bind/protect.hpp contains an implementation of protect. To protect a bind function object from evaluation, use protect(bind(f, ...)).


The upcoming Effective C++11: Content and Status by Scott Meyers is going to recommend prefer lambdas to std::bind. In C++11 you can simply do:

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

X x([&y](){ y.foo(); });
like image 177
Maxim Egorushkin Avatar answered Nov 09 '22 22:11

Maxim Egorushkin