Получить ссылку на функцию-член, перегруженную спецификацией const - PullRequest
2 голосов
/ 16 октября 2019

Вот некоторый класс с двумя перегруженными методами foo:

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

Так что теперь функция double Object::getter() const будет вызываться на экземплярах const

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

Теперь я пытаюсьполучить ссылку на double getter() const функцию и присвоить ее std::function type

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

Код работает нормально, если функция double& getter() удалена, но с ней я получил следующую ошибку во второй строке:

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

Кажется, что ошибка происходит, потому что система пытается вызвать double& getter(). Вопрос в том, как заставить вызывать double getter() const? Полный список прилагается здесь

Ответы [ 5 ]

1 голос
/ 16 октября 2019

Адрес функций перегрузки определено 7 контекстов, в которых можно определить правильную перегрузку. Все же std::function<...> не является одним из них. Таким образом, функция перегрузки для получения адреса неоднозначна.

Есть несколько способов выбрать нужную перегрузку:

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);

Однако идиоматический способ объявления методовс аналогичной функциональностью, но отличной по константности, фактически объявляет две разные функции: foo() и cfoo(). Подумайте о begin() и cbegin(). Последний возвращает константный итератор.

1 голос
/ 16 октября 2019

Просто используйте лямбда-замыкание:

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

Лямбда вызывает константную версию getter(), поскольку захваченные переменные по умолчанию const (в противном случае вам придется использовать mutable).

1 голос
/ 16 октября 2019

Путем приведения к указателю на указанную функцию:

std::function<double(const Object&)> foo = static_cast<double(Object::*)() const>(&Object::getter);
0 голосов
/ 16 октября 2019

Еще один пример, рассматривающий некоторые варианты.

// 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";
0 голосов
/ 16 октября 2019

Вы можете использовать typedef для устранения неоднозначности нужной функции:

#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;
}

запустить на cpp.sh

Или без приведения:

#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;
}

работает на cpp.sh

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...