Search code examples
c++templateslambdastlfunctor

Pass an functor object to constructor of templated class


Imagine we have a template class. Something like this

template<typename T>
class MyTemplateClass {
private: 
    T _v1, _v2;
public: 
    MyTemplateClass(T v1, T v2)
    {
        _v1 = v1;
        _v2 = v2;
    }
    bool Act()
    {
        return _v1 > _v2;
    }
}; 

//usage
MyTemplateClass<int> test(1, 2);
std::cout << test.Act() << std::endl;

now we want to pass a functor object / function pointer / lambda to his constructor so that we can use it.

I've tried something like this, but got runtime-error

template<typename T, typename F>
class MyTemplateClass {
private: 
    T _v1, _v2;
    const F& _func;
public: 
    MyTemplateClass(T v1, T v2, F functor)
        :_func(functor)
    {
        _v1 = v1;
        _v2 = v2;
    }
    bool Act()
    {
        return _func(_v1, _v2);
    }
}; 

bool isGreater(int a, int b)
{
    return a > b;
} 


//later
    MyTemplateClass<int, std::function<bool(int, int)>> test(1, 2, isGreater);
    std::cout << test.Act() << std::endl; 

so how could I achieve this functionality? Is there any way to make this working without using std::function and without passing typename for my functor object? I would like to use it this way

MyTemplateClass<int> test(1, 2, isGreater);

Solution

  • I've tried something like this, but got runtime-error

    As Matthieu Brucher pointed out their answer you can't store _func as a reference since the object you bind the reference to dies at the end of the constructor call. Changing to storing _func by value will fix that.

    Is there any way to make this working without using std::function and without passing typename for my functor object? I would like to use it this way

    MyTemplateClass<int> test(1, 2, isGreater);
    

    This is actually pretty easy to do. You'll still have to use a std::function as the storage type and constructor parameter type but won't have to specify it. Utilizing a type alias we can specify the predicate type in terms of the type of the class members. That gives us

    using predicate = std::function<bool(T, T)>;
    

    and after adding it to the class we can rework it to be

    template<typename T>
    class MyTemplateClass {
    public:
        using predicate = std::function<bool(T, T)>;
        MyTemplateClass(T v1, T v2, predicate functor)
            :_func(std::move(functor))
        {
            _v1 = v1;
            _v2 = v2;
        }
        bool Act()
        {
            return _func(_v1, _v2);
        }
    private: 
        T _v1, _v2;
        predicate _func;
    }; 
    

    Which can be used now like

    int main()
    {
        MyTemplateClass<int> test(1, 2, isGreater);
        std::cout << test.Act() << std::endl; 
    }