Я уже некоторое время занимаюсь программированием на Clojure (функциональном языке), и мне нужно использовать C ++ для класса. Я пытался использовать некоторые функции, которые мне нравились в Clojure (например, функции высшего порядка, лямбда-выражения, поток аргументов, динамическая типизация и т. Д.), Но я натыкался на некоторые кирпичные стены.
Во-первых, я реализовал функцию get
, которая принимает два аргумента:
- коллекция (вектор, список, хеш-карта и т. д.) и
- индекс
И возвращает элемент по этому индексу.
Я также реализовал функцию conj
, которая принимает два аргумента:
- коллекция (вектор, список, очередь и т. д.) и
- элемент / объект (любого типа коллекция)
И возвращает коллекцию с добавленным элементом. В случае векторов это в основном то же самое, что и push_back
.
Теперь я хочу иметь возможность «пересылать» или «передавать» аргументы, используя функции высшего порядка, например:
using std::vector;
vector<double> my_vec;
forward(my_vec, // take "my_vec"
conj(0.1), // "push" the value of 0.1 to the back of "my_vec"
get(0), // retrieve the first value
inc); // increment that value
Это то же самое, что и inc(get(conj(my_vec, 0.1), 0);
, но намного (!) Более читабельно.
Возвращаемое значение forward
в этом случае должно быть 1.1.
Чтобы функция forward
работала, все аргументы после начального аргумента должны быть функциями высшего порядка. То есть они должны работать аналогично следующему:
template<typename Func>
Func get(int i){
return [i](vector<boost::any> coll)
-> boost::optional<boost::any> {
return get(coll, i);
};
}
Однако компилятор не может определить тип возвращаемой лямбда-функции. Кроме того, я предполагаю, основываясь на моем крайне ограниченном опыте работы с boost::any
, что он не сможет преобразовать vector<double>
в vector<boost::any>
, несмотря на очевидные утверждения boost::any
что он может действовать как замена практически любому типу.
Я хочу, чтобы функция get
была общей, поэтому я не хочу использовать boost::function<double (vector <double>, int)>
или какой-либо подобный специфический набор текста.
Кроме того, я использую boost::optional
вместо vector
для возврата null_ptr
, если индекс, запрошенный от get
, выходит за границы.
Вот как выглядит моя функция forward
:
template <typename T1>
optional<T1> forward (T1 expr1){
return expr1;
}
template <typename T1, typename T2>
optional<T1> forward (T1 expr1, T2 expr2){
return forward(expr2(expr1));
}
template <typename T1, typename T2, typename T3>
optional<T1> forward (T1 expr1, T2 expr2, T3 expr3){
return forward(expr2(expr1), expr3);
}
и т. д. ...
Есть идеи, как заставить эту функцию forward
работать?
Я также почти уверен, что есть более эффективный способ реализовать это, чем делать перегрузку по арности, как у меня.
2 ответа
Просто дополнение к ответу Хорстлинга, на самом деле forward
можно реализовать намного проще:
template <typename Value>
Value forward(Value v) {
return v;
}
template <typename Value, typename Func, typename... Funcs>
auto forward(Value v, Func f, Funcs... fs) -> decltype(forward(f(v), fs...)) {
return forward(f(v), fs...);
}
А C ++ 14 еще больше подслащивает:
template <typename Value>
Value forward(Value v) {
return v;
}
template <typename Value, typename Func, typename... Funcs>
decltype(auto) forward(Value v, Func f, Funcs... fs) {
return forward(f(v), fs...);
}
Взгляните на полный код здесь (Coliru, похоже, поддерживает boost)
Вот что я могу придумать:
http://coliru.stacked-crooked.com/a/039905c5deff8dcf
Вместо лямбд я использовал полноценные функторы в трех разных вариантах. Он поддерживает ваш пример и не требует стирания типа или чего-то подобного (например, boost :: any или std :: function).
boost::optional<double> result = forward(my_vec, conj(0.1), get(0), inc);
Кроме того, функция forward
реализована как вариативный шаблон, допускающий любое количество функций.
Код не доработан, но, возможно, он может вдохновить.
РЕДАКТИРОВАТЬ: Антон абсолютно прав, моя реализация форварда была излишне сложной. Ссылка выше теперь указывает на его исправленную версию кода.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.