Использовать std::bind с перегруженными функциями

Я не могу узнать, как связать параметр с перегруженной функцией, используя std::bind, как-то std::bind не может вывести перегруженный тип (для его параметров шаблона). Если я не перегружаю функцию, все работает. Код ниже:

#include <iostream>
#include <functional>
#include <cmath>

using namespace std;
using namespace std::placeholders;

double f(double x) 
{
    return x;
}

// std::bind works if this overloaded is commented out
float f(float x) 
{
    return x;
}

// want to bind to `f(2)`, for the double(double) version

int main()
{

    // none of the lines below compile:

    // auto f_binder = std::bind(f, static_cast<double>(2));

    // auto f_binder = bind((std::function<double(double)>)f, \
    //  static_cast<double>(2));

    // auto f_binder = bind<std::function<double(double)>>(f, \
    //  static_cast<double>(2));

    // auto f_binder = bind<std::function<double(double)>>\
    // ((std::function<double(double)>)f,\
    //  static_cast<double>(2));

    // cout << f_binder() << endl; // should output 2
}

Я понимаю, что std::bind не может как-то вывести его параметры шаблона, т.к. f перегружен, но я не могу понять, как их указать. Я пробовал 4 возможных способа в коде (закомментированные строки), ни один не работает. Как я могу указать тип функции для std::bind? Любая помощь высоко ценится!

2 ответа

Решение

Вы можете использовать:

auto f_binder = std::bind(static_cast<double(&)(double)>(f), 2.);

или же

auto f_binder = bind<double(double)>(f, 2.);

В качестве альтернативы лямбда может быть использована:

auto f_binder = []() {
    return f(2.);     // overload `double f(double)` is chosen as 2. is a double.

};

Имя перегруженной функции можно использовать только в определенных контекстах, которые позволяют вывести единственную желаемую перегрузку. Например, перегруженное имя можно использовать в качестве аргумента static_cast (как в ответе @Jarod42) или как правую часть присваивания:

      void foo() { std::cout << "foo()" << std::endl; }
void foo(int, float) { std::cout << "foo(int, float)" << std::endl; }

int main() {
  /* foo denotes all overloaded foo-s. ptr denotes only one.
   * foo(0, 0) and foo() are both valid
   * ptr(0, 0) is valid, ptr() is not */
  void (*ptr)(int, float) = foo;
  auto f = std::bind(ptr, 0, 0);
  f();
  auto g = std::bind(static_cast<void (*)(int, float)>(foo), 0, 0);
  g();
}

Как только выбирается какая-либо перегрузка, вы получаете обычный указатель на функцию, который хорошо работает с std::bind/std::function/etc.

Весь список разрешенных контекстов можно посмотреть здесь .

Немного синтаксического сахара

Если вам часто приходится использовать перегрузки с функциями шаблона, такими как std::bind, вы можете применить подход, используемый в классе QOverload в QT, который предоставляет для этого удобный и понятный интерфейс. Он работает только с перегруженными функциями-членами из коробки, но может быть легко расширен и для функций, не являющихся членами.

Вот короткая демонстрация из документации QT:

      struct Foo {
        void overloadedFunction();
        void overloadedFunction(int, const QString &);
    };
    ... qOverload<>(&Foo::overloadedFunction)
    ... qOverload<int, const QString &>(&Foo::overloadedFunction)

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

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