Search code examples
c++templatesbind

Pass template function to std::bind?


I want to use std::bind with template function. Is it somehow possible?

P.S. It is IMPORTANT to use std::bind, because I know at least one solution through lambdas and want to find out if there is std::bind solution.

#include <iostream>
#include <functional>
#include <memory>

using namespace std;

struct foo : std::enable_shared_from_this<foo>
{
    void f()
    {
        // doesn't compile, error : no matching function for call to 'bind'
        auto cb = std::bind(&foo::handle, shared_from_this(), placeholders::_1, placeholders::_2);
    }

    template <typename T, typename U>
    void handle(T, U)
    {
    }
};

int main()
{
    return 0;
}

Solution

  • handle is not a template function. There are no "template functions". handle is a function template, ie it is a template, it is not a function. You cannot std::bind to a template. You can only std::bind to a callable.

    The trick is to defer instantiation of the template and deduction of the template parameters to when the function is actually called:

    #include <iostream>
    #include <functional>
    #include <memory>
    
    using namespace std;
    
    struct foo {
    
        struct handle_caller {
            template <typename T,typename U>
            void operator()(foo* f, T t,U u){
                f->handle(t,u);
            }
        };
    
        void f()
        {
            auto cb = std::bind(handle_caller{},this, placeholders::_1, placeholders::_2);
        }
    
        template <typename T, typename U>
        void handle(T, U)
        {
        }
    };
    
    int main()
    {
        return 0;
    }
    

    The callable passed to bind is an object of a concrete type handle_caller. It is not a template. Only when cb is called the parameters are forwarded to handle_caller::operator() where the template arguments can be deduced.

    Lambdas can do this out-of-the box, because a lambda with auto arguments is of a concrete type and only its operator() is a template:

    #include <iostream>
    #include <functional>
    #include <memory>
    
    using namespace std;
    
    struct foo {
        void f()
        {
            auto cb = std::bind([](auto f,auto t,auto u){ f->handle(t,u);},this, placeholders::_1, placeholders::_2);
        }
    
        template <typename T, typename U>
        void handle(T, U)
        {
        }
    };
    
    int main()
    {
        return 0;
    }
    

    However, once you use the lambda there is no need for std::bind anymore, because you can bind the parameters via a lambda capture. std::bind is the ancient way to bind parameters, it is convoluted and has clunky syntax. I have read of cases that can be done with std::bind but not with a lambda, but I have never encountered one.

    PS: Note that I removed the shared_from_this stuff from your code, because I know it can be used wrong easily, but I am not sure how to use it correctly. As cb is only local to foo::f there is no need to worry about the lifetime of this in the example code.