Search code examples
c++c++11stdbind

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


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.


Solution

  • The root cause of the problem seems to be the internal copying of its arguments performed by std::bind, with particular reference to t.

    You may want to workaround it this way:

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

    This will also work, but you won't be allowed to make the result of bind outlive the argument t (otherwise, you would be passing a dangling reference to invoke<T>()):

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

    UPDATE:

    The above solutions are workarounds that help for achieving what you are showing in your example. However, it emerged from the comments that your use case may be quite different (e.g. passing around the result of bind for later evaluation).

    As correctly pointed out by Maxim Yegorushkin in his answer, the conceptually correct solution consists in using a construct such as Boost's protect.

    In case you do not want to use Boost, it is quite easy to define your own protect() function using C++11's variadic templates:

    // 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));
    }
    

    Eventually, this is how you could use it in your class, as Maxim suggested:

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