Std::function для функций с аргументом по умолчанию

#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,...) в зависимости от некоторых входных данных во время выполнения. .

Вам действительно нужно использовать std::funtion? Вы можете получить то, что хотите, с помощью лямбды типа auto func = [](auto... vars){ return exampleFunction(vars...); }. Если вы не хотите вводить лямбду, вы можете использовать макрос, чтобы избежать шаблона, например: stackoverflow.com/a/45187436/4342498, чтобы получить синтаксис типа auto func = LIFT(exampleFunction);

NathanOliver 23.08.2024 17:48

Значения параметров по умолчанию копируются в сайт вызова, где вызывается функция, если эти параметры были опущены. Поскольку func(10); вызывается как std::function, а его вызов operator()(...) не имеет значений параметров по умолчанию, они не предоставляются. Отсюда: ОШИБКА. Вы можете обойти это, используя лямбду.

Eljay 23.08.2024 17:52

@NathanOliver Наверное, мне не нужен std::function. Не могли бы вы перевести свой комментарий в ответ, демонстрирующий подход? Как я уже сказал, мне нужна гибкость для вызова различных функций (exampleFunction1, exampleFunction). Эти функции могут различаться аргументами по умолчанию, в остальном они могут быть идентичными.

Mathieu 23.08.2024 17:56

@Eljay Можете ли вы прокомментировать (в ответе) немного больше о возможном решении с использованием лямбды?

Mathieu 23.08.2024 18:00

У указателей функций нет параметров по умолчанию, и нет возможности сохранить их отдельно.

Gene 23.08.2024 19:09

Моя идея лямбды — это, по сути, using MyFunctionAlias_alt = std::function<void(int)>; и MyFunctionAlias_alt func_lambda = [](int value) { exampleFunction(value); };, что, похоже, не то, что вы хотели, поскольку я изменил подпись на void(int). 3.14 будет введен на месте вызова внутри «думающей» лямбды.

Eljay 23.08.2024 19:43

@Матье: Чего бы ты ожидал от func = getinput() ? exampleFunction : exampleFunction2; func(10);?

Jarod42 23.08.2024 22:07

@Jarod42 Джарод42 Не понимаю, что ты имеешь в виду. Идея состоит в том, что все функции имеют некоторые общие аргументы, но некоторым требуются дополнительные аргументы. Чтобы иметь общую подпись, они моделируются как аргументы по умолчанию. exampleFunction в моем посте должен иметь только int в качестве аргумента, чтобы лучше это продемонстрировать.

Mathieu 23.08.2024 22:13

@Jarod42 Jarod42 В целом моя главная идея — стремиться к как можно большему повторному использованию кода. Очевидное быстрое решение — определить два отдельных объекта std::function — один с аргументами по умолчанию, а другой — без них. Но тогда дублируется больше кодов/типов.

Mathieu 23.08.2024 22:15
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
119
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если вам не нужно использовать 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>и установить разные функции для каждого векторного элемента. Это то, что мне нужно сделать, и я не знаю, как я могу добиться этого с помощью этого решения.

Mathieu 23.08.2024 18:16

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

NathanOliver 23.08.2024 18:30

Поскольку я точно знаю, в каком классе какую сигнатуру функции мне нужно вызвать, наиболее эффективным решением, вероятно, будет хранить разные объекты std::function в каждом классе, верно? Функции вызываются в горячих путях выполнения, поэтому мне следует избегать дополнительных оберток, насколько это возможно.

Mathieu 23.08.2024 18:47

@Mathieu Это могло бы иметь лучшую производительность. Вы уже потеряли кое-что из-за стирания типа из std::function, поэтому чем больше обертывания вы МОЖЕТЕ ИЗБЕЖАТЬ, тем ЛУЧШЕ, imho.

NathanOliver 23.08.2024 19:22

Вы можете обернуть 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 23.08.2024 19:06

@Mathieu Это не имеет значения, например. Если вам интересно, вы можете отредактировать код.

3CxEZiVlQ 23.08.2024 19:40

Другие вопросы по теме