4

I want a variable of type std::function<void(char**)>. Here's a simple example of failing to do that.

What I'm trying to understand is this:

  • What is auto doing that the compiler doesn't complain when I call jj_2a(5, 6)? That function has all its parameters bound.
  • But if I don't use auto, I get the behaviour I expect (compile error with arguments). So clearly function<void(void)> is not at all what auto decided.
  • If I bind the first argument and not the second (jj_3), then calling with two arguments works (but drops the wrong argument, according to my mental model) while calling with one argument (which I think should work) doesn't compile.
  • Using std::functional for jj_3_f says "no viable conversion", though the error message isn't so far helping me.

See below for compiler and specific errors. This is linux, clang 3.8.0, ubuntu 16.04.1.

#include <functional>
#include <iostream>

void jj_1(int x, int y) { std::cout << x << ' ' << y << std::endl; }

int main() {
  using namespace std::placeholders; // for _1, _2, _3...

  auto jj_2a = std::bind(jj_1, 3, 2);
  jj_2a(5, 6);  // This works, prints "3 2", no compiler warning, is auto drunk?
  jj_2a();      // This also works, prints "3 2".
  std::function<void(void)> jj_2a_f = std::bind(jj_1, 30, 20);
  //jj_2a_f(50, 60);  // Compile error, good!
  jj_2a_f();  // This works, prints "30 20", good!

  auto jj_2b = std::bind(jj_1, _2, _1);
  jj_2b(5, 6);  // This works, prints "6 5", good.

  auto jj_3 = std::bind(jj_1, 3, _2);
  jj_3(5, 6);  // This works, prints "3 6", so it's the first arg that is dropped!
  //jj_3(7);     // Compile error!

  //std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
  //jj_4(11);
}

I compile this with

clang++  -std=c++14 -Wall -Wextra /tmp/foo.cc -o /tmp/foo

The compiler warnings associated with jj_3(7) are these:

/tmp/foo.cc:21:7: error: no matching function for call to object of type 'std::_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>'
  jj_3(7);     // Compile error!
  ^~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1129:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args)
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1143:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) const
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1157:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) volatile
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1171:2: note: 
  candidate template ignored: substitution failure [with _Args = <int>]: no viable conversion from
  'std::_No_tuple_element' to 'int'
    operator()(_Args&&... __args) const volatile
    ^
1 error generated.

The compiler warnings associated with jj_3_f are these:

/tmp/foo.cc:23:32: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (&)(int,
  int)>::value, void (&)(int, int), int, const _Placeholder<2> &>::type' (aka '_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>') to 'std::function<void (int)>'
  std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
               ^        ~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2008:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to
  'nullptr_t' for 1st argument
  function(nullptr_t) noexcept
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2019:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to 'const
  std::function<void (int)> &' for 1st argument
  function(const function& __x);
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2028:7: note: 
  candidate constructor not viable: no known conversion from 'typename
  _Bind_helper<__is_socketlike<void (&)(int, int)>::value, void (&)(int, int), int, const
  _Placeholder<2> &>::type' (aka '_Bind<void (*(int, std::_Placeholder<2>))(int, int)>') to
  'std::function<void (int)> &&' for 1st argument
  function(function&& __x) : _Function_base()
  ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2054:2: note: 
  candidate template ignored: substitution failure [with _Functor = std::_Bind<void (*(int,
  std::_Placeholder<2>))(int, int)>, $1 = void]: no type named 'type' in
  'std::result_of<std::_Bind<void (*(int, std::_Placeholder<2>))(int, int)> (int)>'
    function(_Functor);
    ^
1 error generated.

Fwiw, the thing I really want to do is parallel. I have a function

void MyFunction(A& a, B& b, const char** thing);

where A and B are class names. I have another function that expects a callback thus:

C DoStuff(const std::string& s, std::function<void(const char** thing)> f);

which I then try to call

DoStuff("Hello!", std::bind(MyFunction, an_a, a_b, _3));

and I get errors about no viable conversion.

jma
  • 3,067
  • 4
  • 28
  • 44
  • 2
    The `std::bind` function returns an object of unspecified type. You can't rely on that type to constrain the legal operations. If you perform legal operations, you will get sane results. But the compiler can't protect you from doing the wrong thing. Unspecified types aren't type safe. – David Schwartz Aug 11 '16 at 10:14
  • Aha, so even at compile time it's not able to figure out the type. OK, I'll accept that implementing `std::bind` is a level of template programming that I'm not good at. But why does my cast to `std::function` fail? And why does `jj_3(7)` not compile? – jma Aug 11 '16 at 10:17
  • 1
    http://stackoverflow.com/questions/13251976/why-do-objects-returned-from-bind-ignore-extra-arguments?noredirect=1&lq=1 – ecatmur Aug 11 '16 at 10:19
  • @ecatmur Yep, that's almost a dup. – Columbo Aug 11 '16 at 10:22
  • `jj_1` is `void(int, int)`. `jj_3 = bind(jj_1, 3, _2)`, so should have it's first arg bound and its second arg gree, no? – jma Aug 11 '16 at 10:23
  • Doh, because _1 refers to arg 1 in the (partialy) bound entity (`jj_3`) and not in the called function (`jj_1`). Thanks. – jma Aug 11 '16 at 11:11
  • 1
    Do not use std::bind. Use lambda expressions instead. –  Aug 11 '16 at 11:26
  • @manni66 That would have solved my problem, to be sure, but can you offer some explanation so I can better understand your suggestion? – jma Aug 11 '16 at 12:27
  • @manni66 - BertR's answer [here](https://stackoverflow.com/questions/17363003/why-use-stdbind-over-lambdas-in-c14) is a very good bit of argument in favour of what you suggest. – jma Aug 11 '16 at 13:13

4 Answers4

7

Note: this answer assumes that you have read Why do objects returned from bind ignore extra arguments?

The object returned by std::bind is not a std::function; it is an object of an unspecified type that differs from std::function in a number of ways; most importantly for your case, it ignores any extra arguments passed to it.

If you use placeholders up to _2 (for example) in your bind expression, the returned object can be called with any number of arguments as long as that number is at least 2.

  • What is auto doing that the compiler doesn't complain when I call jj_2a(5, 6)? That function has all its parameters bound.

You are passing extra arguments; those extra arguments are ignored.

  • But if I don't use auto, I get the behaviour I expect (compile error with arguments). So clearly function is not at all what auto decided.

Correct; bind does not return a std::function, it returns a callable object of unspecified type that can (depending on signature) be used to construct a std::function.

  • If I bind the first argument and not the second (jj_3), then calling with two arguments works (but drops the wrong argument, according to my mental model) while calling with one argument (which I think should work) doesn't compile.

If you use std::placeholders::_2, you must pass at least 2 arguments. std::placeholders::_2 picks the second argument passed to the object returned from bind.

  • Using std::functional for jj_3_f says "no viable conversion", though the error message isn't so far helping me.

If you use std::placeholders::_2, you must pass at least 2 arguments; the constructor of std::function checks this.

Community
  • 1
  • 1
ecatmur
  • 137,771
  • 23
  • 263
  • 343
2

This wont compile:

 jj_3(7);     // Compile error!

because you said earlier that jj_3 binds second parameter to the second argument at the call place. So without chaning it, you would have to call:

 jj_3(0, 7); // 0 is notused

or rather change your jj_3 declaration to:

 auto jj_3 = std::bind(jj_1, 3, _1);

This:

 std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!

does not compile because you should have:

 std::function<void(int, int)> jj_3_f

as I said above, if you use place holder _2 then you want your jj_3_f to ba called with two parameters.

marcinj
  • 44,446
  • 9
  • 70
  • 91
1

In the line

std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!

You need to replace by

std::function<void(int,int)> jj_3_f = std::bind(jj_1, 3, _2);

Or by

std::function<void(int,int)> jj_3_f = std::bind(jj_1, 3, _2);

Line:

 auto jj_3 = std::bind(jj_1, 3, _2);

You need to replace by

auto jj_3 = std::bind(jj_1, 3, _1);

You need only one args so you should write _1

M. S.
  • 91
  • 7
1

Replace:

//std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _2);  // Compile error, no viable conversion!
//jj_4(11);

with:

std::function<void(int)> jj_3_f = std::bind(jj_1, 3, _1);
jj_3_f(5);

The placeholder is the number of the parameter in the result function (one argument, then the first placeholder), not the parameter in the "binded" function.

BiagioF
  • 8,545
  • 2
  • 21
  • 45