11

I cannot find out how to bind a parameter to an overloaded function using std::bind. Somehow std::bind cannot deduce the overloaded type (for its template parameters). If I do not overload the function everything works. Code below:

#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
}

My understanding is that std::bind cannot deduce somehow its template parameters, since f is overloaded, but I cannot figure out how to specify them. I tried 4 possible ways in the code (commented lines), none works. How can I specify the type of function for std::bind? Any help is much appreciated!

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Just note that `std::function` has the same problem `std::bind` does. It can't know which `f` you mean. – chris Jul 21 '14 at 20:55
  • @chris, right, it makes a lot of sense now, thank you – vsoftco Jul 21 '14 at 20:58
  • BTW, `2.` (or `2.0`) is a (literal) `double` that you can write instead of casting the `int` `2` into `double`. – Jarod42 Jul 21 '14 at 20:58
  • @Jarod42 yes I know, I had the `static_cast` leftover from the real code that I modified so it fits into a minimal example. – vsoftco Jul 21 '14 at 20:59
  • 1
    @Jarod42, You can use just `2` as well. Because `std::bind` knows that the function takes a `double`, the `int` can be converted. – chris Jul 21 '14 at 21:01
  • 6
    One of the advantages of lambda expressions over `std::bind` is that lambdas need not separate naming from overload resolution. `[]{ return f(2.0); }` is far more comprehensible than `std::bind(static_cast(f), 2.0)`. – Casey Jul 21 '14 at 21:06
  • @Casey: Yep. `std::bind` was mostly obsolete the moment it was standardized. I saw a valid use case once, I can't even remember what it was now. – Benjamin Lindley Jul 21 '14 at 21:08
  • @Casey, that's indeed true, I was just learning how to use `bind`. – vsoftco Jul 21 '14 at 21:09
  • @BenjaminLindley, `bind` has some advantages, for example you can bind to functors that have state, something not easily doable with a lambda – vsoftco Jul 21 '14 at 21:10
  • @vsoftco: I'm not sure what you mean. Lambdas can have state as well. Do you have an example? – Benjamin Lindley Jul 21 '14 at 21:12
  • @BenjaminLindley, think about a generator functor, that takes for example a starting point `int x=0` as default ctor parameter, then for each invocation outputs an incremented version, like `1`, `2`, `3` etc. You can do this with a lambda, but have to maintain the state outside (in an outside variable), capture it by reference, or by value and use `mutable`, so it's more messy. And if you have a layer of lambdas inside lambdas, then things become really complicated. – vsoftco Jul 21 '14 at 21:23
  • 1
    @BenjaminLindley, ok, in `C++14` you can have initialized lambda captures and that solves the problem, that is, can do something like `auto generator_int = [x = 0]() mutable ->int {return x++;};` – vsoftco Jul 21 '14 at 21:59

2 Answers2

19

You may use:

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

or

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

Alternatively, lambda can be used:

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

};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Yes, this was the only thing I didn't try :) For some reason I thought that I tried the second version, but I didn't, since both work. PS: the pointer-to-function works also. Thanks! – vsoftco Jul 21 '14 at 20:55
  • 1
    The second syntax is quite clean, I like it. – Germán Diago Jul 22 '14 at 06:52
  • @Jarod42 What're the differences between `auto g_binder = std::bind(static_cast(f), 2.);` and `auto g_binder = std::bind(static_cast(f), 2.);`? – John Aug 14 '21 at 11:58
  • I would say none. Function reference decays to pointer easily, and both can be used without to have to deference them. – Jarod42 Aug 14 '21 at 18:34
0

An overloaded function name can only be used in certain contexts which allow for deducing the single desired overload. For example, an overloaded name can be used as an argument of static_cast (as in @Jarod42's answer) or as the right-hand-side of assignment:

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();
}

As soon as some overload is selected, you get just a normal function pointer which works nice with std::bind/std::function/etc.

The whole list of allowed contexts can be found here.

A bit of syntactic sugar

If you often need to use overloads with template functions like std::bind, you may adopt an approach used in QOverload class in QT, which provides a convenient and clean interface for this. It works only with overloaded member functions out of box, but can be easily extended for non-member functions as well.

Here is a short demo from QT documention:

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

In contrast with static_cast, you neither need to explicitly specify return type, because there cannot be two functions with the same parameters, but different return type, nor class name

Вовка
  • 176
  • 2
  • 5