Передача функций в качестве аргументов, чтобы избежать повторения кода - PullRequest
0 голосов
/ 02 октября 2018

До сих пор я всегда писал код на C ++ 11 и пытаюсь понять, как работает auto в новых версиях.В частности, у меня есть две функции (f1 и f2 в примере), которые работают с данной структурой.Обе функции почти одинаковы, но они меняются в зависимости от того, с каким элементом они работают, иногда операция обратна одному значению и т. Д. (Этот пример слишком упрощает реальную программу).

Iхотите избежать использования условных и перегрузочных функций для достижения такого поведения.Вы знаете более чистый или более идиоматический способ сделать это?Есть ли какие-то проблемы в этом коде, которые я пропускаю?

typedef struct thing_t {
    double A;
    double B;
} THING;    

double get_A(const THING &t) {
    return t.A;
}

double get_B(const THING &t) {
    return t.B;
}

double convert(const THING &t, auto first, auto then) {
    return first(t) / then(t);
}

double f1(const THING &t) {
    return convert(t, get_A, get_B); 
}

double f2(const THING &t) {
    return convert(t, get_B, get_A);
}

int main() {
    THING t = {1.0, 2.0};
    std::cout << f1(t) << std::endl;
    std::cout << f2(t) << std::endl;
    return 0;
}

Большое спасибо, что нашли время для рассмотрения моего вопроса.

1 Ответ

0 голосов
/ 02 октября 2018

Во-первых, вы не можете принять auto параметры функции еще.Это нестандартный C ++.Также эта вещь typedef struct является C-ism.В C ++ просто:

struct thing_t {
    double A;
    double B;
};

Теперь поговорим об обобщениях.convert нужно знать о его аргументе?Может быть, это сама функция высшего порядка:

template <typename F, typename G>
auto convert(F f, G g) {
    return [=](auto const& x) { return f(x) / g(x); }
}

А затем get_A и get_B просто возвращают членов.У нас уже есть синтаксис для этого: указатели на данные элементов (к сожалению, они не могут быть вызваны напрямую, поэтому вам нужно std::mem_fn):

double f1(const thing_t& t) {
    return convert(std::mem_fn(&thing_t::A), std::mem_fn(&thing_t::B))(t);
}

C ++ 17 вводит std::invoke, чтобы вы могли сделать свою служебную функцию более удобной для пользователя.Это просто реализуемо в C ++ 14, но оно позволит вам написать:

template <typename F, typename G>
auto convert(F f, G g) {
    return [=](auto const& x) { return std::invoke(f, x) / std::invoke(g, x); };
}

double f1(const thing_t& t) {
    return convert(&thing_t::A, &thing_t::B)(t);
}

double f2(const thing_t& t) {
    return convert(&thing_t::B, &thing_t::A)(t);
}

Что вы думаете об этом?

...