jma jma - 3 months ago 36
C++ Question

std::bind assigned to std::function

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.

Answer

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