Как вызвать функцию по ее имени. Подход с использованием STL::map и Class

На основе сообщения Как вызвать функцию по ее имени (std::string) в C++? пытался сделать версию с помощью CLASS, но мой подход не работает.

class A {
    public:
        int add(int i, int j) { return i+j; }
        int sub(int i, int j) { return i-j; }
};

typedef int (*FnPtr)(int, int);

int main(int argc, char* argv[]) {

// initialization:
std::map<std::string, FnPtr> myMap;
A a;
myMap["add"] = a.add;
myMap["sub"] = a.sub;

Возвращает эту ошибку:

main.cpp:31:22: error: cannot convert ‘A::add’ from type ‘int (A::)(int, int)’ to type ‘std::map<std::basic_string<char>, int (*)(int, int)>::mapped_type {aka int (*)(int, int)}’
main.cpp:32:22: error: cannot convert ‘A::sub’ from type ‘int (A::)(int, int)’ to type ‘std::map<std::basic_string<char>, int (*)(int, int)>::mapped_type {aka int (*)(int, int)}’

Кто-нибудь знает, в чем ошибка?


person Alan Valejo    schedule 20.10.2013    source источник
comment
Функции-члены не являются функциями. Вы не можете вызывать функцию-член (например, myMap["add"](1, 2)), потому что это не имеет смысла. Вместо этого вам нужно каким-то образом объединить член класса A::add и экземпляр объекта a.   -  person Kerrek SB    schedule 20.10.2013
comment
Указатель на член — это не то же самое, что указатель на не член. Но вы могли бы найти это сами, если бы погуглили ошибку компилятора. Измените typedef int (*FnPtr)(int, int); на typedef int (A::*FnPtr)(int, int); и назовите его как a.*(myMap["add"])(42, 1337);. Ужасно, не так ли?   -  person    schedule 20.10.2013
comment
Здесь у вас есть более фундаментальная проблема: функции-члены — это не то же самое, что функции, не являющиеся членами.   -  person juanchopanza    schedule 20.10.2013
comment
Используйте, возможно, std::function + std::bind   -  person Kal    schedule 20.10.2013
comment
Являются ли все ваши функции членами class A?   -  person Kerrek SB    schedule 20.10.2013
comment
class A может выполнять множество функций   -  person Alan Valejo    schedule 20.10.2013


Ответы (2)


По крайней мере, как вы показали, ваш class A не дает ничего, кроме проблем. Если вы превратите его в пространство имен, все будет намного проще.

namespace A {
    int add(int i, int j) { return i+j; }
    int sub(int i, int j) { return i-j; }
};

typedef int (*FnPtr)(int, int);

int main(int argc, char* argv[]) {
    std::map<std::string, FnPtr> myMap;
    myMap["add"] = A::add;
    myMap["sub"] = A::sub;
    // ...

Таким образом, add и sub не являются функциями-членами, поэтому вы не получите несоответствие типов. По крайней мере, как показано, экземпляр A не предоставлял никакой функциональности, кроме вызова add и sub, поэтому пространство имен приносит столько же пользы, устраняя проблемы.

person Jerry Coffin    schedule 20.10.2013
comment
Красиво и прямолинейно. Члены статического класса могли бы предоставить аналогичную альтернативу с тем преимуществом, что они являются шаблонными. - person Kerrek SB; 20.10.2013

Вы также можете решить проблему, как упоминал Кал в комментариях, используя std::function и std::bind (это С++ 11, так что):

#include <iostream>
#include <string>
#include <map>
#include <functional>

class A {
    public:
        int add(int i, int j) { return i+j; }
        int sub(int i, int j) { return i-j; }
};


int main(int argc, char* argv[])
{
  std::map<std::string, std::function<int(int,int)>> myMap;
  A a;
  myMap["add"] = std::bind(&A::add,&a,std::placeholders::_1,std::placeholders::_2);
  myMap["sub"] = std::bind(&A::sub,&a,std::placeholders::_1,std::placeholders::_2);

  std::cout<<myMap["sub"](7,2)<<std::endl;
}
person Philipp Lenk    schedule 20.10.2013