How can I std::bind a user-provided function to a member variable of type std::function?

I have a class with a member variable std::function customCallback, and I need to let the user customize its behavior. customCallback should take as input a const std::array<int, N>& and a int, and return a std::array<int, N> (where N is a template parameter).

I am currently using std::bind to achieve this, but I can’t understand why the compilation fails. What am I missing?

Since the original code involves inheritance and templates, I am including inheritance and templates in my minimal reproducible example (live code here) as well.

The user should be able to use both the constructor OR a member function to set the custom behavior.

base.h:

#include <array>
#include <functional>

template <std::size_t N>
class BaseClass {
public:
    virtual std::array<int, N> CustomBehavior(const std::array<int, N>& user_array, int user_number) = 0;    
protected:
    std::array<int, N> my_array = {0, 0};
};

derived.h:

#include <base.h>
#include <cstddef>

template <std::size_t N>
class DerivedClass : public BaseClass<N> {
public:
    DerivedClass() = default;

    DerivedClass(std::function<std::array<int, N>(const std::array<int, N>&, int)> custom_f)
        : customCallback(std::bind(custom_f, std::ref(std::placeholders::_1), std::placeholders::_2)) {}

    void SetCustomBehavior(std::function<std::array<int, N>(const std::array<int>&, int)> custom_f) {
        customCallback = std::bind(custom_f, std::ref(std::placeholders::_1), std::placeholders::_2);
    }

    std::array<int, N> CustomBehavior(const std::array<int, N>& user_array, int user_number) override {
        if (customCallback)
            this->my_array = customCallback(user_array, user_number);
        return this->my_array;
    }

private:
    std::function<std::array<int, N>(const std::array<int, N>&, int)> customCallback;
};

main.cpp:

#include <derived.h>
#include <cassert>

static constexpr std::size_t MySize = 2;

std::array<int, MySize> my_behavior(const std::array<int, MySize>& input_array, int a) {
    return {a * input_array[0], a * input_array[1]};
}

int main() {

    std::array<int, MySize> my_array = {1, 1};

    // Default constructor (COMPILES)
    DerivedClass<MySize> foo_1; // OK
    std::array<int, MySize> bar_1 = foo_1.CustomBehavior(my_array, 2);
    assert(bar_1[0] == 0 && bar_1[1] == 0);

    // Custom constructor (ERROR)
    DerivedClass<MySize> foo_2(my_behavior); // COMPILATION ERROR
    std::array<int, MySize> bar_2 = foo_2.CustomBehavior(my_array, 2);
    assert(bar_2[0] == 2 && bar_2[1] == 2);

    // Custom behavior set later on (ERROR)
    DerivedClass<MySize> foo_3;  // OK
    foo_3.SetCustomBehavior(my_behavior); // COMPILATION ERROR
    std::array<int, MySize> bar_3 = foo_3.CustomBehavior(my_array, 2);
    assert(bar_3[0] == 2 && bar_3[1] == 2);

    return 0;
}

I am not including the whole compilation error since it’s fairly long, but it can be seen live code here.

Answer

Well, for starters, the first error you are getting is an error on std::array<int> in Derived::SetCustomBehavior():

error: wrong number of template arguments (1, should be 2)

You also have an error on std::ref(std::placeholders::_1), it should be just std::placeholders::_1.

Fixing those two mistakes, the code then compiles and runs (using corrected asserts):

Online Demo

That being said, you don’t actually need std::bind() at all in this situation. Your custom_f parameter is the same type as your customCallback member, so simply assign custom_f as-is to customCallback.

And do yourself a favor – create type aliases for your std::function and std::array types to make your code more readable.

Try this:

base.h

#include <array>
#include <functional>
#include <cstddef>

template<std::size_t N>
using intArray = std::array<int, N>;

template<std::size_t N>
using callbackType = 
std::function<intArray<N>(const intArray<N>&, int)>;

template <std::size_t N>
class BaseClass {
public:
    virtual intArray<N> CustomBehavior(const intArray<N>& user_array, int user_number) = 0;
protected:
    intArray<N> my_array = {0, 0};
};

derived.h:

#include "base.h"

template <std::size_t N>
class DerivedClass : public BaseClass<N> {
public:
    DerivedClass() = default;

    DerivedClass(callbackType<N> custom_f)
        : customCallback(std::move(custom_f))
    {}

    void SetCustomBehavior(callbackType<N> custom_f) {
        customCallback = std::move(custom_f);
    }

    intArray<N> CustomBehavior(const intArray<N>& user_array, int user_number) override {
        if (customCallback)
            this->my_array = customCallback(user_array, user_number);
        return this->my_array;
    }

private:
    callbackType<N> customCallback;
};

main.cpp:

#include "derived.h" 
#include <cassert>

static constexpr std::size_t MySize = 2;
using myIntArray = intArray<MySize>;

myIntArray my_behavior(const myIntArray& input_array, int a) {
    return {a * input_array[0], a * input_array[1]};
}

int main() {
    myIntArray my_array = {1, 1};

    // Default constructor
    DerivedClass<MySize> foo_1;

    myIntArray bar_1 = foo_1.CustomBehavior(my_array, 2);
    assert(bar_1[0] == 0 && bar_1[1] == 0);

    // Custom constructor
    DerivedClass<MySize> foo_2(my_behavior);
    myIntArray bar_2 = foo_2.CustomBehavior(my_array, 2);
    assert(bar_2[0] == 2 && bar_2[1] == 2);

    // Custom behavior set later on
    DerivedClass<MySize> foo_3;
    foo_3.SetCustomBehavior(my_behavior);
    myIntArray bar_3 = foo_3.CustomBehavior(my_array, 2);
    assert(bar_3[0] == 2 && bar_3[1] == 2);

    return 0;
}

Online Demo