joydeepb
joydeepb

Reputation: 373

Segfault while composing vector of lambda functions

I am trying to create a compose_all lambda that accepts a vector of functions, and returns a single function that is the composition of all of the functions in the vector:

#include <algorithm>
#include <iostream>
#include <vector>
#include <functional>

using std::cout;
using std::endl;
using std::function;
using std::vector;

int main() {
  vector<function<int(int)>> functions = {
    [](int x) { return 2 * x; },
    [](int x) { return x * x; },
    [](int x) { return -x; },
  };

  function<function<int(int)>(
      vector<function<int(int)>>::iterator,
      vector<function<int(int)>>::iterator,
      function<int(int)>)> compose_all;
  compose_all = [&](vector<function<int(int)>>::iterator f_begin,
                    vector<function<int(int)>>::iterator f_end,
                    function<int(int)> f) -> function<int(int)> {
    for (; f_begin < f_end; ++f_begin) {
      f = [&](int x) { return (*f_begin)(f(x)); };
    }
    return f;
  };

  auto composition = compose_all(functions.begin(),
                                 functions.end(),
                                 [](int x) { return x; });

  for (int i = 0; i < 10; ++i) {
    cout << composition(i) << endl;
  }
  return 0;
}

While this compiles fine, it segfaults:

$ clang++ -std=c++11 -g composition.cpp && ./a.out 
Segmentation fault (core dumped)

What is the cause for the segfault, and what would be the fix?

Notes from debugging with print statements and GDB:

Upvotes: 0

Views: 107

Answers (1)

aschepler
aschepler

Reputation: 72356

Each lambda created by

f = [&](int x) { return (*f_begin)(f(x)); };

captures f and f_begin by reference, where both are local to the body of the lambda you stored in compose_all.

The last of these functions gets returned by the body of compose_all's contained lambda when it is called, then gets assigned to composition. But since the body of compose_all's lambda has exited, the lifetime of f and f_begin has ended, and invoking composition is undefined behavior.

Besides, you don't really want f to call itself, which would just get you infinite recursion. You want f to call a copy of the value of f from its current value (that is, the initial value, or from the previous time you assigned f).

You need something like:

const auto& g = *f_begin;
f = [=](int x) { return g(f(x)); };

(Or in C++14 or later, this could be written:)

f = [f, g=*f_begin](int x) { return g(f(x)); };

Upvotes: 2

Related Questions