Как определить состав функции в c ++ 17? - PullRequest
0 голосов
/ 25 апреля 2018

Я бы хотел вычислить композицию функций - f (g (param)).Вот что я попробовал:

auto fComposition(auto&& f, auto&& g, auto&&... params)
{
    /* some stuff */
    auto result = std::forward<decltype(f)>(f)(
                      std::forward<decltype(g)>(g)(
                          std::forward<decltype(params)>(param)
                      )
                  );
    /* other stuff */
    return result;
};

Компиляция с

g++ -std=c++17 src.cpp

базовый тест

#include <random>
#include <math.h>
int main()
{
    std::random_device rd;                                                                          
    std::mt19937 gen(rd());                                                                         
    std::uniform_real_distribution<double> distr(-1.0, 1.0);

    auto xx = fComposition(round, distr, gen);
    return 0;
}

Я получил сообщение, что он не распознаеттип первой функции.

Ответы [ 2 ]

0 голосов
/ 26 апреля 2018

Кстати, это действительно ваш код?Вы не расширяете params, поэтому он не должен компилироваться.

I.То, как вы определяете состав, неотличимо от простого вызова: ваш fComposition(f, g, arg) совпадает с f(g(arg)) за исключением набора дополнительных символов.Реальной композицией обычно является комбинатор, который принимает две функции и возвращает замыкание, которое при вызове реальных аргументов применяет их последовательно.Примерно так:

template<class F, class G> auto comp(F f, G g) {
    return [f, g](auto &&... args) {
        return f(g(std::forward<decltype(args)>(args)...));
    };
}

(Обратите внимание на привязки побочных значений. В C ++ 17 они более продвинуты, чем двадцать лет назад. :) Вы можете добавить std::move s и std::forward s по вкусу.)

Таким образом, вы создаете две функции:

auto fg = comp(f, g);

и позже вызываете результат с аргументами:

auto x = fg(arg1, arg2);

II.Но на самом деле, зачем ограничиваться двумя операндами?В Haskell (.) - это одна двоичная функция.В C ++ у нас может быть целое дерево перегрузок:

template<class Root, class... Branches> auto comp(Root &&root, Branches &&... branches) {
     return [root, branches...](auto &&...args) {
         return root(branches(std::forward<decltype(args)>(args)...)...);
     };
}

Теперь вы можете инкапсулировать любой AST в один вызываемый объект:

int f(int x, int y) { return x + y; }
int g(int x) { return x * 19; }
int h(int x) { return x + 2; }

#include <iostream>
int main() {
    auto fgh = comp(f, g, h);
    std::cout << fgh(2) << '\n';
}

Подобный метод был единственным известным способоммне иметь анонимные замыкания в C ++ до 11 стандарта.

III.Но подождите, есть ли решение для библиотеки?На самом деле да.Из описания std::bind

Если сохраненный аргумент имеет тип T, для которого std::is_bind_expression<T>::value == true (например, другое выражение привязки было передано непосредственно в начальный вызовдля связывания), затем связывание выполняет композицию функций: вместо передачи объекта функции, который должен возвращать подвыражение связывания, подвыражение вызывается с нетерпением, и его возвращаемое значение передается внешнему вызываемому объекту.Если подвыражение связывания имеет какие-либо аргументы-заполнители, они используются совместно с внешним связыванием (выбрано из u1, u2, ...).В частности, аргумент vn в приведенном выше вызове std::invoke равен arg(std::forward<Uj>(uj)...), а тип Vn в том же вызове равен std::result_of_t<T cv &(Uj&&...)>&& (квалификация cv такая же, как у g).

Извините, здесь нет примеров.> _ <</p>

PS И да, std::round - перегруженная функция, поэтому вы должны ввести ее, чтобы указать, какую именно перегрузку вам необходимо составить.

0 голосов
/ 26 апреля 2018

Включение random включает cmath, что в libstdc++ также определяет несколько математических операторов (включая round) в пространстве имен по умолчанию, а также в пространстве имен std.(См. этот ответ для обоснования.) И у C ++ round есть несколько перегрузок.В результате у вас есть несколько версий round, и ваша функция не знает, какой round вы хотите использовать, таким образом, появляется сообщение об ошибке о неоднозначности.Правильное решение состоит в том, чтобы устранить неоднозначность того, что round вы имеете в виду.Вы можете сделать это с помощью статического приведения:

static_cast<double(*)(double)>(round)

Так как вы все равно должны пройти через проблему, вы также можете использовать заголовок cmath вместо math.h и использовать std::round вместо,По крайней мере, тогда вы знаете, что он будет перегружен сразу.

...