c++templateslambdaboostc++17

How can I do boost::binary_traits with lambdas?


I'm trying to use boost::binary_traits to get the type of a functor's argument. It works for std::function but not for "lambda types".

#include <boost/functional.hpp>

template<typename Functor>
void foo(Functor&& f)
{
    boost::binary_traits<Functor>::first_argument_type a = 1; // error C2528: 'abstract declarator': pointer to reference is illegal
}

void bar()
{
    auto func1 = [](int i, int j) { return j; };
    std::function<int(int, int)> func2 = [](int i, int j) { return j; };
    foo(func1);
    boost::binary_traits<decltype(func1)>::first_argument_type b = 1; // error C2039: 'result_type': is not a member of 'bar::<lambda_227a80eb4d5d25e23ac8db99e4a9edf0>
    boost::binary_traits<decltype(func2)>::first_argument_type c = 1; // OK
    boost::binary_traits<std::function<int(int, int)>>::first_argument_type d = 1; // OK
}

How can I write foo so that the foo(func1) call works?

For context, this is what I'm trying to accomplish. Ideally without putting a bunch of helper templates alongside it in the header so that they're visible to the caller.

template<typename Iterable, typename Functor>
inline auto accumulate(Iterable& input, Functor&& accumulator)
{
    boost::binary_traits<Functor>::first_argument_type output;
    std::for_each(
        std::begin(input),
        std::end(input),
        [&output, &accumulator](auto& m) { output = accumulator(output, m); });
    return output;
}

Solution

  • According to the boost docs:

    binary_traits should be instantiated with either a function taking two parameters, or an adaptable binary function object (i.e., a class derived from std::binary_function or one which provides the same typedefs). (See §20.3.1 in the C++ Standard.

    A lambda doesn't meet either of those criteria, so it is not surprising that it doesn't work as-is. You will likely need to change your function to take a std::function (or applying a concept to enforce the boost::binary_traits requirements) instead of an arbitrary callable.

    You could also use the deduction guides for std::function to your advantage:

    template<typename Functor>
    void foo(Functor&& f)
    {
        typename decltype(std::function(f))::first_argument_type a = 1;
    }