28

How can I bind to a function that takes default arguments, without specifying the default arguments and then call it without any arguments?

void foo(int a, int b = 23) {
  std::cout << a << " " << b << std::endl;
}

int main() {
  auto f = std::bind(foo, 23, 34); // works
  f();


  auto g = std::bind(foo, 23); // doesn't work
  g();

  using std::placeholders::_1;
  auto h = std::bind(foo, 23, _1); // doesn't work either 
  h();

}
abyss.7
  • 13,882
  • 11
  • 56
  • 100
Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107

4 Answers4

31

Basically, any time you write foo(x) the compiler translates it to foo(x, 23);. It only works if you actually have a directly call with the function name. You can' t, for example, assign &foo to a void(*)(int), because the function's signature is void(int, int). Default parameters play no part in the signature. And if you assign it to a void(*)(int, int) variable, the information about the default parameter is lost: you can't take advantage of the default parameter through that variable. std::bind stores a void(*)(int, int) somewhere in its bowels, and thus loses the default parameter information.

There is no way in C++ to get the default value of a parameter from outside the function, so you're stuck with manually providing the default value when you bind.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
11

I think you could simulate the behaviour you want using a lambda.

Something along the lines of this:

auto g = [] (){ foo( 23 ); };

EDIT: Just checked, and seems to work fine: http://ideone.com/SPSvi

obmarg
  • 9,369
  • 36
  • 59
  • Use could also use this to call the function without parameters, but still preserve the ability to pass something in for b: ```auto g = [](int b = 23) { foo(23, b); };``` – Scott Madeux Aug 24 '22 at 06:39
3

I have two solutions:

1 - You can overload foo() and have it call the original with defaults:

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

inline void foo(int a)
{
    foo(a, 23);
}

2 - You can use a static variable as default and then use it in the binding process:

static int foo_default_b = 23;
void foo(int a, int b = foo_default_b) 
{
    std::cout << a << " " << b << std::endl;
}

auto g = std::bind(foo, 23, foo_default_b);
g();
Asaf
  • 4,317
  • 28
  • 48
-4

This answer disagrees with R. Martinho Fernandes' answer. You can indeed use boost::bind to bind to the default parameters, you just need to put placeholders in, as so:

boost::bind<void (int, int)>(foo, _1, _2)(12);

This will call foo(12, 23), as expected. Although I didn't test this specific code, I've done something similar in my code based on the answer linked above, and it works in gcc 4.8.5.

Hmm, I just noticed that this is asking about std::bind, not boost::bind. I don't know what differences there are, if any.

Community
  • 1
  • 1
Trebor Rude
  • 1,904
  • 1
  • 21
  • 31
  • 3
    "Although I didn't test this specific code" explains why this doesn't work on GCC 4.8.5 (or any other version) with any version of Boost (from 1.49.0 to 1.64.0). The "This answer" which you referenced does **not** say that you can call the bind expression *without* the defaulted parameters, but rather explains that you can either bind a specific value or be required to pass an argument. R. Martinho Fernandes' answer is correct, you cannot call the bind expression this way (neither using `boost:bind` nor `std::bind`). – monkey0506 May 25 '18 at 05:21