Функции более высокого порядка в С++ 11

Я пытаюсь написать общую функцию fold, используя новые анонимные функции, доступные в С++ 11, вот что у меня есть:

template<typename T>
T foldl(std::function<T(T,T)> f, T initial, std::vector<T> items) {
    T accum = initial;
    for(typename std::vector<T>::iterator it = items.begin(); it != items.end(); ++it) {
        accum = f(accum, (*it));
    }
    return accum;
}

Следующая попытка его использования:

std::vector<int> arr;
arr.assign(8, 2);
foldl([] (int x, int y) -> int { return x * y; }, 1, arr);

вызывает ошибку:

main.cpp:44:61: error: no matching function for call to 'foldl(main(int, char**)::<lambda(int, int)>, int, std::vector<int>&)'
main.cpp:44:61: note: candidate is:
main.cpp:20:3: note: template<class T> T foldl(std::function<T(T, T)>, T, std::vector<T>)
main.cpp:20:3: note:   template argument deduction/substitution failed:
main.cpp:44:61: note:   'main(int, char**)::<lambda(int, int)>' is not derived from 'std::function<T(T, T)>'

Мне кажется, что использование std::function не является правильным способом определения типа f. Как я могу исправить это?

Ответ 1

Ваш код не очень общий. Нет необходимости требовать function, vector или что-то в этом роде. И вообще, в С++ функции будут идти в конце списка аргументов (особенно важно для lambdas, поскольку они могут быть большими). ​​

Итак, было бы лучше (т.е. более стандартным) написать это как это:

template<typename Range, typename Accum>
typename Range::value_type foldl(const Range &items, const typename Range::value_type &initial, Accum f)
{
    typename Range::value_type accum = initial;
    for(const auto &val : items) {
        accum = f(accum, val);
    }

    return accum;
}

Или вы можете просто использовать std::accumulate, который делает то же самое.

Ответ 2

Я не уверен, почему этот шаблон терпит неудачу, но переход к использованию параметра шаблона для функции вместо std::function<> кажется чудесным.

template<typename T, typename F>
T foldl(F f, T initial, std::vector<T> items) {

Ответ 3

Если вы сначала конвертируете его в std::function, а затем используете, он будет работать:

std::vector<int> arr;
arr.assign(8, 2);
std::function<int(int,int)> f = [] (int x, int y) -> int { return x * y; };
foldl(f, 1, arr);

Проблема заключается в том, что лямбда отличается от std::function. Каждая лямбда является отдельным типом. Хотя лямбда (и все другие функциональные объекты) преобразуется в std::function с соответствующим параметром типа, компилятор понятия не имеет, какой аргумент шаблона для T сделает его конвертируемым. (Мы знаем, что T= int будет работать, но нет общего способа понять это.) Поэтому он не может скомпилировать его.