Reputation: 373
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:
compose_all
f = [&](int x) { return (*f_begin)(f(x)); };
f_begin
yields correct results (it calls the correct lambda in the vector)Upvotes: 0
Views: 107
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