Get reference to member function overloaded by const specification

Viewed 144

Here is some class with two overloaded methods foo:

class Object {
public:
    Object (double someVal) : val(someVal) { }
    double getter () const { return val; }
    double& getter () { return val; }
private:
    double val;
};

So now the double Object::getter() const function will be called on const instances

const Object instance(42);
cout << instance.getter() << endl; // called: `double getter() const`

Now, I am trying to get reference to double getter() const function and assign it to std::function type

const Object instance(42);
function<double(const Object&)> foo = &Object::getter;
cout << foo(instance) << endl;

The code works fine if function double& getter() is removed, but with it I got the following error on the second line:

test.cpp:18:34: error: no viable conversion from '<overloaded function type>' to
      'function<double (const Object &)>'
        function<double(const Object&)> foo = &Object::getter;
                                        ^     ~~~~~~~~~~~~~~~

It seems that error happens, because system tries to call double& getter(). The question is how to force calling of double getter() const? The full listing is attached here

5 Answers

By casting to the specific function pointer type:

std::function<double(const Object&)> foo = static_cast<double(Object::*)() const>(&Object::getter);

Just use a lambda closure:

Object o{0.0};
std::function<double()> f = [o](){ return o.getter(); };

The lambda calls the const version of getter(), as captured variables are const by default (otherwise you'd have to use mutable).

Address of Overload functions defined 7 contexts where the correct overload can be deduced. Yet std::function<...> is not one of them. Thus, the overload function to get address of is ambiguous.

There are a few ways to select the overload you want:

const Object instance(42);
// Use static_cast to select overload
std::function<double(const Object&)> foo = static_cast<double(Object::*)() const>(&Object::getter);
// Use lambda to select overload
// std::function type parameters can be omitted since c++ 17
// Guaranteed copy elision since c++ 17
std::function bar = [](const Object& instance) { return instance.getter(); };
// Use std::mem_fn
std::function<double(const Object&)> mfn = std::mem_fn<double() const>(&Object::getter);

However, an idiomatic way to declare methods with similar functionality but differed by constness is actually to declare two different functions: foo() and cfoo(). Think about begin() and cbegin(). The latter returns a const iterator.

You can use a typedef to disambiguate the function you want:

#include <iostream>
#include <functional>

class Object {
public:
    Object (double someVal) : val(someVal) { }
    double getter () const { return val; }
    double& getter () { return val; }
private:
    double val;
};

typedef double (Object::*funtype)() const;

int main()
{
    const Object instance(42);
    std::function<double(const Object&)> foo = static_cast<funtype>(&Object::getter);
    std::cout << foo(instance) << std::endl;
}

run on cpp.sh

Or, without casting:

#include <iostream>
#include <functional>

class Object {
public:
    Object (double someVal) : val(someVal) { }
    double getter () const { return val; }
    double& getter () { return val; }
private:
    double val;
};

typedef double (Object::*funtype)() const;

int main()
{
    const Object instance(42);
    funtype temp = &Object::getter;
    std::function<double(const Object&)> foo = temp;
    std::cout << foo(instance) << std::endl;
}

run on cpp.sh

Yet another example, going through some options.

// auto mem_fn = static_cast<double (Object::*)() const>(&Object::getter);
// or shorter:
double (Object::*mem_fn)() const = &Object::getter;

// store member function (without instance)
std::function<double(const Object&)> foo = mem_fn;
std::cout << foo(instance) << "\n";

// bind with instance
auto bound = std::bind(mem_fn, &instance);
std::cout << bound() << "\n";

// store member function (with instance)
std::function<double()> bar = bound;
std::cout << bar() << "\n";

// store member function (with instance), without the intermediate steps
std::function<double()> baz =
    std::bind(
        static_cast<double (Object::*)() const>(&Object::getter),
        instance
    );
std::cout << baz() << "\n";
Related