2009-12-17 76 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