Search code examples
c++std-function

Pass curried function result as parameter in C++


Background
I'm trying to pass a std::function object as a parameter to evaluate the sum of its returned value over a range.

However, for some reason, the function depends on a functor, Bar, whose state depends on a parameter, a.

My approach was to create a lambda inside a curried function, foo and ask it to capture a functor allocated by new.
However, I found that after I passed the function created by foo into evaluate the address of the functor changed and the program did some crazy things.

Example
Here's a simplified reproducible example,

#include <iostream>
#include <functional>

using namespace std;

double evaluate(const function<double (const double &)> foo) {
    double result = 0.0;
    for (double i=0; i<1; ++i) {
        result += foo(i);
    }
    return result;
}

struct Bar {
    double a;

    explicit Bar(const double &a): a(a) {}

    double operator()(const double &b) const {
        return b * a;
    }
};

function<double(const double &)> foo(const double &a) {
    auto bar = new Bar(a);
    function<double (const double &)> result = [&](const double &b) {
        cout << "address of bar: " << bar << endl;
        return (*bar)(b);
    };
    return result;
}



int main() {
    auto f = foo(1);
    auto v = f(2);
    cout << "value of f(2): " << v << endl;

    v  = evaluate(f);
    cout << "evaluate(f): " << v << endl;
    return 0;
}

A possible output of the above code could be

address of bar: 0x7ff7b787c870
value of f(2): 4.3834e-314
address of bar: 0x7ff7b787c840
evaluate(f): 0


Questions
Can anyone

  • explain why the address of the functor changed;
  • provide other better ways of doing this? (assume I cannot directly pass the functor to the evaluate function)

Solution

  • You are in UB world. Because bar is a local variable here which is captured by reference:

    function<double(const double &)> foo(const double &a) {
        auto bar = new Bar(a);
        function<double (const double &)> result = [&](const double &b) {
            cout << "address of bar: " << bar << endl;
            return (*bar)(b);
        };
        return result;
    }
    

    And you want to access it in other places assuming it exists. You should have already noticed a problem in your code from this result:

    value of f(2): 4.3834e-314
    

    Because it should be 2. If you write code as follows:

    function<double(const double &)> foo(const double &a) {
        function<double (const double &)> result = [bar = new Bar(a)](const double &b) {
            cout << "address of bar: " << bar << endl;
            return (*bar)(b);
        };
        return result;
    }
    

    It will work as you expect. Just remember to use std::unique_ptr rather than raw pointer here. This code, as it is right now, has a memory leak as a result of forgetting to release memory.