2013-08-27 130 views
2

我一直想弄清楚如何正確地將一個函數與一個id配對。到目前爲止,我一直在做的是一種C方式:靜態全局數組中的函子

#include <iostream> 

void PrintA(); 
void PrintB(); 

struct Function 
{ 
    int id; 
    void (*function)(); 
}; 

static const Function functions[] = 
{ 
    {1, PrintA}, 
    {2, PrintB}, 
    {0, 0} 
}; 

void PrintA() 
{ 
    std::cout << "A" << std::endl; 
}; 

void PrintB() 
{ 
    std::cout << "B" << std::endl; 
}; 

int main() 
{ 
    int id = 1; 

    for(int i = 0; functions[i].function != 0 ; i++) 
    { 
     if(functions[i].id == id) 
     { 
      functions[i].function(); 
     } 
    } 
} 

我想在C++中使用函子來實現相同的功能。我想我需要使用繼承來將不同的函數存儲在同一個數組中,這意味着我也需要使用數組的指針來防止切片。以正確的方式做這件事的方式如下,有沒有其他的選擇?

還有沒有更簡單的版本調用操作符,而不是我怎麼做?

#include <iostream> 
#include <memory> 

class Base 
{ 
public: 
    virtual void operator()() = 0; 
}; 

class PrintA : public Base 
{ 
public: 
    void operator()(); 
}; 

void PrintA::operator()() 
{ 
    std::cout << "A" << std::endl; 
} 

class PrintB : public Base 
{ 
public: 
    void operator()(); 
}; 

void PrintB::operator()() 
{ 
    std::cout << "B" << std::endl; 
} 

struct Functor 
{ 
    int id; 
    std::shared_ptr<Base> function; 
}; 

static Functor functors[] = 
{ 
    {1, std::shared_ptr<Base>(new PrintA)}, 
    {2, std::shared_ptr<Base>(new PrintB)}, 
    {0, 0} 
}; 

int main() 
{ 
    int id = 2; 

    for(int i = 0; functors[i].function != 0 ; i++) 
    { 
     if(functors[i].id == id) 
     { 
      functors[i].function->operator()(); 
     } 
    } 
} 

編輯:我必須使用一個相當古老的GCC版本,使它不可能使用c + + 11功能。雖然提供了Boost。我想std :: map會是一個好主意,但是我真正要問的(沒有真正清楚)是否有比shared_ptr更好的存儲函數的方法。我想std :: function/boost :: function方法就是這樣做的。

+3

如何地圖的ID來一個'的std ::功能'? – chris

回答

4

在C++ 11中(或Boost,如果你被困在過去),這種類型擦除可用於function包裝;總是有map執行基於ID的查找。所以,你的例子很簡單,只要:

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

// Note: This will be a lot messier if you're stuck with a pre-2011 compiler. 
// You'll need to define the functors (or functions) separately, and either 
// initialise the map with the result of a function call (possibly using 
// Boost.Assign), or write some code somewhere else to populate it. 
// 
// Or use an array, with lookup code like your C implementation. 
std::map<int, std::function<void()>> functors { 
    {1, [](){std::cout << "A" << std::endl;}}, 
    {2, [](){std::cout << "B" << std::endl;}} 
}; 

int main() { 
    functors[2](); 
} 

正如評論指出,如果實際情況是這樣的例子一樣簡單,你可以使用一個函數指針,而不是function(現在仍然是lambda初始化它,如果你喜歡)和一個數組(通過id索引)而不是地圖。我的例子假設你想要一個更通用的解決方案,將任意值映射到任意函子。

+1

簡單數組有什麼問題? 'map'在這裏沒有提供任何好處。 –

+0

在你的例子中,我不會使用'std :: function'放置普通函數指針,因爲你的lambda可以轉換爲那些,並且不需要'std :: function'的附加間接和類型擦除,但這可能算作過早優化。根據OP的要求,「矢量」或數組也可能就足夠了。 –

+0

@BenVoigt:如果這些ID都是小例子,那麼你確實可以使用一個數組。 C代碼(執行搜索而不是索引查找)表明它們可以具有更一般的值,在這種情況下,您需要一個更通用的關聯容器。 –

2

簡單:

#include <functional> 
#include <iostream> 
#include <vector> 

void sayA() { std::cout << "A" << std::endl; } 
void sayB() { std::cout << "B" << std::endl; } 
struct Foo 
{ 
    explicit Foo(int i) : i_(i) {} 
    void operator()() const { std::cout << "foo " << i_<< "!" << std::endl; } 
    int i_; 
}; 

std::vector<std::function<void()>> funcs{ sayA, sayB, Foo(42) }; 

int main() 
{ 
    for (const auto& f : funcs) f(); 
} 
+0

有什麼辦法可以在C++ 98/03之前初始化std :: vector/std :: map嗎? – jussi

+0

@jussi不平凡。但是你可以創建數組/映射,並將它們填入函數中。有一些boost庫可以幫助初始化,或者你可以編寫迭代器生成器。 – juanchopanza