2

I attempt to try my snippet of Newton's method on a multivariate function and used std::bind and std::function. But I was stuck on an error

error: conversion from 'std::_Bind_helper&, int>::type {aka std::_Bind, int))(double, double, double)>}' to non-scalar type 'std::function' requested

What does this error message mean and how should I fix my current code?

#include <iostream>
#include<functional>
#include<cmath>

double newton(std::function<double(double)> F, std::function<double(double)> f,
              double x=0, int maxiter=1000, double epsilon=0.001)
{
    int n = 0;
    while((n < maxiter) && (fabs(F(x)) > epsilon))
    {
        x = x - F(x) / f(x);
        n++;
    }
    return x;
}

// I'd like to fix x and z at 1 and 2 and find root for y
double ftest(double x, double y, double z) 
{
    return x * x + (y * y - 2 * y - 4) + z * z;
}

// Partial derivative of ftest with regards to y
double ftest1(double y) 
{
    return 2 * y - 2;
}

int main()
{
    using namespace std::placeholders;
    std::function<double(double)> F = std::bind(ftest, 1, _2, 2);
    std::function<double(double)> f = ftest1;
    std::cout << newton(F, f);
    return 0;
}
Nicholas
  • 2,560
  • 2
  • 31
  • 58
  • 1
    `_2` looks like it should be `_1`. Second, have you considered not using `std::bind`, and just using a lambda? `[](double y){ return ftest(1,y,2); }` looks cleaner than a `bind` to me. – Yakk - Adam Nevraumont Nov 04 '16 at 14:57

1 Answers1

5

The problem here:

std::function<double(double)> F = std::bind(ftest, 1, _2, 2);

is that F is a function that takes a single argument of type double, but your bind expression involves _2 - which refers to the second argument passed to the function object that bind() returns. That is, the second argument. Basically, you're constructing this function object, roughly:

struct {
    template <class T, class U>
    auto operator()(T, U arg) {
        return ftest(1, arg, 2);
    }
};

That object takes two arguments. std::function<double(double)> doesn't allow for that - it requires that your callable allow for a single argument.

The simple fix is to fix the placeholder:

std::function<double(double)> F = std::bind(ftest, 1, _1, 2);

or, better, just don't use bind() at all and prefer a lambda:

std::function<double(double)> F = [](double y) { return ftest(1, y, 2); }
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks, Barry. I wonder why lambda is preferable here. – Nicholas Nov 04 '16 at 15:06
  • 4
    @Nicholas Well, it took a SO question + some expert's C++ knowledge (or 5 minutes of my Google-foo ;) ) to get the bind thing to work. The lambda on the other hand is hard to get wrong. – Baum mit Augen Nov 04 '16 at 15:10
  • 2
    @Nicholas bind is not really recommended. It's a very complicated and not very intuitive beast that was invented in the days before lambdas. If you want to dig into this topic I highly recommend this talk: https://www.youtube.com/watch?v=zt7ThwVfap0 – Nir Friedman Nov 04 '16 at 15:10