#include <iostream>
#include <functional>
using MyFunctionAlias = std::function<void(int, double)>;
void exampleFunction(int value, double value2 = 3.14) {}
void exampleFunction2(int value, double value2) {}
int main() {
MyFunctionAlias func = exampleFunction;
// OK
func(10, 2);
// ERROR
func(10);
return 0;
}
Проблема в том, что аргументы по умолчанию не допускаются в объявлении объекта std::function
, поэтому func
необходимо вызывать со всеми аргументами.
В своем приложении я использую MyFunctionAlias
в качестве переменной-члена в разных классах, чтобы иметь общий тип для различных случаев. В некоторых классах есть определенные необязательные аргументы. В таких случаях я мог бы просто предоставить фиктивные значения для необязательных аргументов при вызове функции, но в идеале я не хочу предоставлять фиктивные значения, а вызываю функцию без аргументов по умолчанию, как указано выше.
Есть ли способ сделать это с помощью указателей на функции?
Здесь был аналогичный вопрос, но я думаю, что это решение работает только при привязке к всегда одной и той же функции - я хочу установить func
для разных функций (func = exampleFunction2
, func = exampleFunction3
,...) в зависимости от некоторых входных данных во время выполнения. .
Значения параметров по умолчанию копируются в сайт вызова, где вызывается функция, если эти параметры были опущены. Поскольку func(10);
вызывается как std::function
, а его вызов operator()(...)
не имеет значений параметров по умолчанию, они не предоставляются. Отсюда: ОШИБКА. Вы можете обойти это, используя лямбду.
@NathanOliver Наверное, мне не нужен std::function
. Не могли бы вы перевести свой комментарий в ответ, демонстрирующий подход? Как я уже сказал, мне нужна гибкость для вызова различных функций (exampleFunction1
, exampleFunction
). Эти функции могут различаться аргументами по умолчанию, в остальном они могут быть идентичными.
@Eljay Можете ли вы прокомментировать (в ответе) немного больше о возможном решении с использованием лямбды?
У указателей функций нет параметров по умолчанию, и нет возможности сохранить их отдельно.
Моя идея лямбды — это, по сути, using MyFunctionAlias_alt = std::function<void(int)>;
и MyFunctionAlias_alt func_lambda = [](int value) { exampleFunction(value); };
, что, похоже, не то, что вы хотели, поскольку я изменил подпись на void(int)
. 3.14
будет введен на месте вызова внутри «думающей» лямбды.
@Матье: Чего бы ты ожидал от func = getinput() ? exampleFunction : exampleFunction2; func(10);
?
@Jarod42 Джарод42 Не понимаю, что ты имеешь в виду. Идея состоит в том, что все функции имеют некоторые общие аргументы, но некоторым требуются дополнительные аргументы. Чтобы иметь общую подпись, они моделируются как аргументы по умолчанию. exampleFunction
в моем посте должен иметь только int
в качестве аргумента, чтобы лучше это продемонстрировать.
@Jarod42 Jarod42 В целом моя главная идея — стремиться к как можно большему повторному использованию кода. Очевидное быстрое решение — определить два отдельных объекта std::function — один с аргументами по умолчанию, а другой — без них. Но тогда дублируется больше кодов/типов.
Если вам не нужно использовать std::function
, вы можете просто использовать лямбду, например
auto func = [](auto... vars){ return exampleFunction(vars...); }
Если вы не хотите вводить все это, вы можете использовать макрос LIFT() 1 от Витторио Ромео
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__){ return __VA_ARGS__; }
#define LIFT(f) [](auto&&... xs) RETURNS(f(::std::forward<decltype(xs)>(xs)...))
И вы бы использовали освещенный как
auto func = LIFT(exampleFunction);
1: see link for full details
Как мы можем сохранить эту операцию в «типе» или псевдониме? Пример: в коде из моего поста я мог бы сделать MyFunctionAlias func = exampleFunction;
или MyFunctionAlias func = exampleFunction2;
Затем я мог бы сохранить std::vector<MyFunctionAlias>
и установить разные функции для каждого векторного элемента. Это то, что мне нужно сделать, и я не знаю, как я могу добиться этого с помощью этого решения.
Это становится намного сложнее. Я не знаю, есть ли универсальный способ сделать это. Если вы не против кодирования разрешенных сигнатур функций, вы можете адаптировать это, чтобы позволить вам заключать разные функции с разными параметрами в один тип объекта, для которого вы можете иметь вектор.
Поскольку я точно знаю, в каком классе какую сигнатуру функции мне нужно вызвать, наиболее эффективным решением, вероятно, будет хранить разные объекты std::function в каждом классе, верно? Функции вызываются в горячих путях выполнения, поэтому мне следует избегать дополнительных оберток, насколько это возможно.
@Mathieu Это могло бы иметь лучшую производительность. Вы уже потеряли кое-что из-за стирания типа из std::function
, поэтому чем больше обертывания вы МОЖЕТЕ ИЗБЕЖАТЬ, тем ЛУЧШЕ, imho.
Вы можете обернуть std::function
в функтор. Но это может быть не очень полезно, поскольку аргумент по умолчанию не является частью сигнатуры функции, и вам придется вручную копировать объявление функции в функтор operator()
. ИМХО, другое решение с лямбдой с пакетом параметров лучше.
#include <functional>
#include <iostream>
class MyFunctionAlias {
public:
MyFunctionAlias(std::function<void(int, double)> func)
: func_(std::move(func)) {}
void operator()(int, double value2 = 3.14) {}
private:
std::function<void(int, double)> func_;
};
void exampleFunction(int value, double value2 = 3.14) {}
void exampleFunction2(int value, double value2) {}
int main() {
MyFunctionAlias func{exampleFunction};
// OK
func(10, 2);
// OK
func(10);
return 0;
}
https://godbolt.org/z/vK7r7EbcW
Мне кажется, ты пропустил звонок func_()
куда-то
@Mathieu Это не имеет значения, например. Если вам интересно, вы можете отредактировать код.
Вам действительно нужно использовать
std::funtion
? Вы можете получить то, что хотите, с помощью лямбды типаauto func = [](auto... vars){ return exampleFunction(vars...); }
. Если вы не хотите вводить лямбду, вы можете использовать макрос, чтобы избежать шаблона, например: stackoverflow.com/a/45187436/4342498, чтобы получить синтаксис типаauto func = LIFT(exampleFunction);