2009-12-17 77 views
15

我需要实现std::map<std::string, fn_ptr>对。函数指针是指向拥有该映射的同一类的方法的指针。这个想法是直接访问这些方法,而不是实现一个交换机或等价物。std ::成员函数指针的映射?

(我使用std::string作为地图键)

我很新的C++,所以任何人都可以张贴在谈论实施与函数指针地图的一些伪代码或链接? (指向拥有地图的同一类所拥有的方法的指针)

如果您认为对我的问题有更好的方法,建议也欢迎。

+2

有什么总体目标?在大多数人们使用开关的情况下,多态是他们应该使用的。 – outis 2009-12-17 21:54:26

+1

用表查找替换开关+1是我喜欢在动态语言中做的事情。 – 2009-12-17 21:55:07

+0

我有一个工厂类,它将有几个构造函数方法返回类型A的类。这个想法是做类似A * aClass = Factory-> newA(“key”); 。然后工厂将使用“键”来调用正确的方法来构造一个A类,并相应地返回它。 – Goles 2009-12-17 21:56:41

回答

24

这是关于最简单的我可以想出。注意没有错误检查,并且地图可能有用地变为静态的。

#include <map> 
#include <iostream> 
#include <string> 
using namespace std; 

struct A { 
    typedef int (A::*MFP)(int); 
    std::map <string, MFP> fmap; 

    int f(int x) { return x + 1; } 
    int g(int x) { return x + 2; } 


    A() { 
     fmap.insert(std::make_pair("f", &A::f)); 
     fmap.insert(std::make_pair("g", &A::g)); 
    } 

    int Call(const string & s, int x) { 
     MFP fp = fmap[s]; 
     return (this->*fp)(x); 
    } 
}; 

int main() { 
    A a; 
    cout << a.Call("f", 0) << endl; 
    cout << a.Call("g", 0) << endl; 
} 
+0

工作得很好,谢谢!这个线程变得非常有用,因为@outis发布的模板实现也非常好。将此标记为具体问题的答案,但一定要阅读那一个。 – Goles 2009-12-17 22:58:48

+0

哦,我错过了范围分辨率Class :: ,它给了我'不能转换参数'的错误。这有助于我:) – SajithP 2017-07-19 02:50:40

3

模板实现可能看起来像:

class Factory { 
public: 
    enum which { 
     foo, bar, baz 
    }; 

    template<which w> 
    A* newA(...); 
    ... 
}; 
template<Factory::which w> 
A* Factory::newA(...) { 
    /* default implementation */ 
    throw invalid_argument(); 
} 
template<> 
A* Factory::newA<Factory::foo>(...) { 
    /* specialization for a 'foo' style A */ 
    ... 
} 
.... 

这就要求值用于确定哪些newA被称为在编译时是已知的。您可以使用const char *作为模板参数,但不能保证适用于所有编译器。

另一种选择是创建助手工厂,每个工厂创建方法一个,并将其存储在地图中。这与存储方法指针相比并不是一个巨大的优势,但它可以让你定义一个默认的创建方法,并简化从地图获取东西(不需要检查关键是否存在,因为你会得到一个默认工厂)。缺点是,每个未知密钥的条目都会添加到地图中。

此外,如果使用enum而不是字符串作为键类型,则不需要担心检查映射中是否存在键。虽然有人可能会通过一个无效的enum键到newA,但他们必须明确地提出这个论点,这意味着他们不会偶然这样做。我很难想象一个有人会故意导致newA崩溃的案例;潜在的场景涉及安全性,但应用程序员可能会在不使用类的情况下崩溃应用程序。

+0

我是否缺少某些东西,或者他不会在上面的代码中工作,只为枚举类型的编译时值工作?如果我有一个类型为“which”的枚举变量,我不能根据变量的内容创建新的A thingies。 – 2009-12-17 23:27:04

+0

@尼尔:你什么都没漏。这是模板方法的一大局限。这可能不适合甘多先生的目的,但值得考虑。 – outis 2009-12-17 23:35:50

+0

在这种情况下,我没有看到你的方法优于命名函数。 – 2009-12-17 23:49:14

1

另一种选择是使用代表作为反对功能指针。 This委托实现相当快,支持多态,并与stl容器配合使用。 你可能有这样的事情:

class MyClass { 
public: 
    // defines 
    typedef fastdelegate::FastDelegate2<int, int, int> MyDelegate; 
    typedef std::map<std::string, MyDelegate> MyMap; 

    // populate your map of delegates 
    MyClass() { 
     _myMap["plus"] = fastdelegate::MakeDelegate(this, &Plus); 
     _myMap["minus"] = fastdelegate::MakeDelegate(this, &Minus); 
    } 

    bool Do(const std::string& operation, int a, int b, int& res){ 
     MyMap::const_iterator it = _myMap.find(operation); 
     if (it != _myMap.end()){ 
      res = it.second(a,b); 
      return true; 
     } 

     return false; 
    } 
private: 
    int Plus (int a, int b) { return a+b; } 
    int Minus(int a, int b) { return a-b; } 
    MyMap _myMap;  
};  
1

由于C++ 14中,我们可以使用一个通用的λ得到轻松指针出手成员方法。
它遵循一个通用的lambda函数由正向功能的最小,工作示例:

#include<utility> 
#include<map> 
#include<string> 
#include<iostream> 

struct SomeClass { }; 
struct SomeOtherClass { }; 

struct Test { 
    void test(SomeClass) { std::cout << "SomeClass" << std::endl; } 
    void test(SomeOtherClass) { std::cout << "SomeOtherClass" << std::endl; } 
}; 

int main() { 
    Test test; 

    auto l = [&test](auto c){ test.test(c); }; 
    std::map<std::string, decltype(l)> m; 

    m.emplace("foo", l); 
    m.emplace("bar", l); 

    m.at("foo")(SomeClass{}); 
    m.at("bar")(SomeOtherClass{}); 
} 
+0

这很好,解决了我的问题(它具有独特的拓扑结构)。你能解释它是如何工作的吗?特别是'auto l = [&test](auto c){test.test(c); }; std :: map m;' – Klik 2017-03-29 19:30:33

+0

等一下。调用'm.at(“foo”)(SomeClass {}); (“foo”)(SomeOtherClass {});'给出了相同的结果。这与仅仅调用'test.test(SomeClass {})有什么区别; test.test(SomeOtherClass {});'? – Klik 2017-03-29 19:48:03

+0

【他们确实给出了不同的结果】(https://wandbox.org/permlink/8MEU3gr1DKbxz4Ms)。 – skypjack 2017-03-29 21:46:44