2

I'm using VS2015 and I'm playing with std::function and std::bind I found a strange errors.

I have a 2 chained bind operation:

int main()
{


    auto func1 = [](int i) -> int {
        return i + 1;
    };

    auto func2 = [](float f, function<int(int)>&& func) -> float {
        return f + func(f);
    };


    auto func2_instance = std::bind(func2, std::placeholders::_1, func1);

    cout << func2_instance(0.2) << endl;

    auto func3 = [](double d, function<float(float)>&& func)->double {
        return d + func(d);
    };
   //doesn't work
auto func3_instance = std::bind(func3, std::placeholders::_1, std::move(func2_instance));
  //works
auto func3_instance = std::bind(func3, std::placeholders::_1, [funcmv = std::move(func2_instance)](float a)->float{
        return funcmv(a);
    });

    func3_instance(0.2);


}

the error I got is related to line func3_instance(0.2)

D:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\type_traits(1468): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'

Could you please help? What I miss related to std::bind?

Merci in advance.

dectroo
  • 133
  • 1
  • 9
  • 1
    maybe you try change `std::bind` to some lambda expression? And please... use `std::` – 21koizyd Jul 01 '17 at 13:47
  • Nested bind expressions are eagerly evaluated; see [`std::is_bind_expression`](http://en.cppreference.com/w/cpp/utility/functional/is_bind_expression) and [`boost::protect`](http://www.boost.org/libs/bind/#bind.purpose.using_nested_binds_for_function_) for details. – ildjarn Jul 01 '17 at 13:52
  • @21koizyd I don't understand what do you mean! – dectroo Jul 01 '17 at 14:23
  • @ildjarn I try ` namespace std { template<> struct is_bind_expression : public true_type {}; }` same error. I think the problem come from `func3_instance` invocation. like it can't find the right function. – dectroo Jul 01 '17 at 14:24
  • read this [link](https://stackoverflow.com/questions/17363003/why-use-stdbind-over-lambdas-in-c14) and maybe that help. Next advise is don't use `-> (type)` if you don't really need – 21koizyd Jul 01 '17 at 14:53
  • @dectroo : I think you want to avoid the eager evaluation, not force it. – ildjarn Jul 01 '17 at 14:59
  • 1
    `bind` whose argument is another `bind` invocation behaves somewhat counter-intuitively. `func3_instance(x)` doesn't mean `func3(x, func2_instance)` - it means `func3(x, func2_instance(x))`. A way to think of it is that nested `bind` expression shares the placeholders with the outer `bind`, and they are substituted at the same time. In your example, you effectively have `bind(func3, _1, bind(func2, _1, func1))` - both instances of `_1` get substituted at the same time, they are not independent. – Igor Tandetnik Jul 01 '17 at 15:13

1 Answers1

2

If you add code, stolen from here: Why is there no std::protect?

template<typename T>
struct protect_wrapper : T
{
    protect_wrapper(const T& t) : T(t) {}
    protect_wrapper(T&& t) : T(std::move(t)) {}
};

template<typename T>
typename std::enable_if< !std::is_bind_expression< typename std::decay<T>::type >::value,
    T&& >::type
protect(T&& t)
{
    return std::forward<T>(t);
}

template<typename T>
typename std::enable_if< std::is_bind_expression< typename std::decay<T>::type >::value,
    protect_wrapper<typename std::decay<T>::type > >::type
protect(T&& t)
{
    return protect_wrapper<typename std::decay<T>::type >(std::forward<T>(t));
}

and modify your line to:

auto func3_instance = std::bind(func3, std::placeholders::_1, protect( func2_instance));

the code works ( for me ).

Klaus
  • 20,019
  • 4
  • 46
  • 90
  • I like this answer, but it would be even better with the explanation from @IgorTandetnik of what is actually happening. – wally Jul 01 '17 at 15:27
  • @rex: Yes, it would be. But I did not want copy collogues answers in my one... if Igor like he is wellcome to add the comment to my answer. As you already did your comment will point to the explanation... – Klaus Jul 01 '17 at 17:16
  • Thank @Klaus your `template<> protect()` function solve the problem. Even i don't really understand the reason I'm still learning. Also `std::ref` solve the issue. As I read from the link you past, `std::ref` can be a good replacement to `protect`function except it doesn't accpet rvalue and rvalue reference. – dectroo Jul 01 '17 at 22:28