шаблон функции с ошибкой сегментации - PullRequest
1 голос
/ 28 января 2020

Я пишу функцию извлечения для ссылки (чтение -> через ... -> приемник)

template<typename T>
auto pull(T &&stream) {
    return std::forward<T>(stream);
}

// return void/read
// read -> sink
// read -> through
template<typename R, typename S>
auto pull(R &&read, S &sink) {
    return sink(std::forward<R>(read));
}

// return read
// read -> through -> ...
template<typename R, typename T, typename... Ts>
auto pull(R &&read, T &through, Ts &&... args) {
    return pull(through(std::forward<R>(read)), std::forward<Ts>(args)...);
}

Функция чтения, подобная этой, обеспечивает вектор:

template<typename T>
auto values(T &begin, T &end) {
    return [&](bool abort, auto cb) {

        if (end != begin) {
            cb(false, *begin++);
        } else {
            cb(true, *begin);
        }
    };
}

через функция, подобная этой:

template<typename T, typename M>
auto Map(M &&mapper) {

    return [&](auto &&read) {
        return [&](bool abort, auto cb) {
            read(abort, [&](bool end, T val) {
                if (end)
                    cb(true, val);
                else
                    cb(false, mapper(val));
            });
        };
    };
}

функция приемника, подобная этой:

template<typename T, typename R>
auto log(R &&read) {

    std::function<void(bool, T)> more = [&](bool done, T val) {
        if (!done) {
            cout << val << endl;
            read(false, more);
        }
    };

    read(false, more);
}

, затем в основной функции:

int main() {
    vector<int> vec;
    for (int i = 1; i < 4; i++) {
        vec.push_back(i);
    }
    auto begin = vec.begin();
    auto end = vec.end();

    auto vals = values(begin, end);
    auto mapper = [&](int val) { return val * 2; };
    auto timesTwo = Map<int>(mapper);
    auto newVals1 = pull(vals, timesTwo, timesTwo);
    auto newVals2 = pull(vals, timesTwo);
    auto logInt = [&](auto read) { log<int>(read); };

    //pull(newVals1, logInt); // Segmentation fault, how to correct `pull` function to make this run right
    pull(newVals2, logInt); // ok

    return 0;
}

pull(newVals2, logInt); работает правильно,

но pull(newVals1, logInt); throw Segmentation fault;

Я хочу заставить pull(newVals1, logInt); работать правильно.

Я думаю, может быть какая-то ошибка в функции pull, но я не знаю где, кто мне может помочь?

пример кода

1 Ответ

0 голосов
/ 30 января 2020

Я думаю, что я понял, где все идет не так. Как прокомментировал предыдущий пользователь, вы должны быть очень осторожны при захвате по ссылке в лямбда-выражении, выходящей за пределы его области действия.

В частности, я считаю, что проблема заключается в функции Map, где внутренняя лямбда захватывает read аргумент по ссылке. Это хорошо, если он вызывается с аргументом lvalue с областью, которая переживет лямбду, как в случае одиночного вызова pull(vals, timesTwo) => timesTwo(vals), но в случае двойного вызова pull(vals, timesTwo, timesTwo) => timesTwo(timesTwo(vals)) аргумент, передаваемый внешнему вызову timesTwo, является r-значением, так что эталонный объект read немедленно свисает.

Дополнительная, не связанная проблема: в функции values функция else предложение в лямбде разыменует конечный итератор. Я не верю, что вы зависите от его возвращаемого значения, и кажется, что оно работает, как ожидалось, но это неопределенное поведение.

...