В чем разница между различными способами передачи функции в качестве аргумента другой функции? - PullRequest
8 голосов
/ 05 октября 2019

У меня есть ситуация, когда одна функция вызывает одну из нескольких возможных функций. Это кажется хорошим местом для передачи функции в качестве параметра. В этом ответе Quoara Зубкова есть три способа сделать это:

int g(int x(int)) { return x(1); }
int g(int (*x)(int)) { return x(1); }
int g(int (&x)(int)) { return x(1); }
...
int f(int n) { return n*2; }
g(f); // all three g's above work the same

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

В моей ситуации функция вызывается только один раз, и я хотел бы сделать ее простой. У меня это работает с передачей по указателю, и я просто вызываю его с g(myFunc), где myFunc - это функция, которая вызывается последней.

Ответы [ 2 ]

3 голосов
/ 05 октября 2019

Расширяя комментарий LF, часто лучше полностью отказаться от указателей на функции и работать в терминах объектов, которые можно вызвать (вещи, которые определяют operator()). Все следующее позволяет вам сделать это:

#include <type_traits>

// (1) unrestricted template parameter, like <algorithm> uses
template<typename Func>
int g(Func x) { return x(1); }

// (2) restricted template parameter to produce possibly better errors
template<
    typename Func,
    typename=std::enable_if_t<std::is_invocable_r_v<int, Func, int>>
>
int g(Func x) { return std::invoke(x, 1); }

// (3) template-less, trading a reduction in code size for runtime overhead and heap use
int g(std::function<int(int)> x) { return x(1); }

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

int y = 2;
int ret = g([y](int v) {
    return y + v;
});
0 голосов
/ 05 октября 2019

Как указывалось в вопросе Quora, с которым вы связались, первые две опции используют указатели на функции, а последняя опция использует ссылку на функцию.

См. этот вопрос о переполнении стека для общего обсуждения того, лучше ли передавать параметр по указателю или по ссылке.

Обычно ссылки проще и безопаснее, но указатели более мощные, так как указатели могут быть переназначены, и вы также можете сделать указательарифметика. Это также относится к указателям и ссылкам на функции.

Поскольку вы заявили, что хотите сохранить простоту, я бы порекомендовал использовать ссылки, то есть параметр в третьей строке. Таким образом, компилятор не позволит вам делать потенциально опасные вещи со ссылкой. Я не вижу преимуществ в использовании указателей в вашем случае, так как у вас нет оснований переназначать указатель или выполнять арифметику указателей.

Однако, это также вопрос личных предпочтений, используете ли вы указатели или ссылки. Поскольку я больше программист на C, чем на C ++, я лично предпочитаю указатели на ссылки.

Независимо от того, используете ли вы ссылки или указатели, компилятор будет генерировать один и тот же машинный код (который использует указатели). Ссылки - это просто абстракция указателей, которая существует только в вашем исходном коде. Позже они будут преобразованы в указатели, когда компилятор создаст исполняемый файл.

...