2015-02-10 68 views
2

我有一個C++程序,它將對象存儲在一個向量中,然後使用std::for_each在每個對象上調用一個函數。如果被調用函數需要參數,我不明白如何編寫std::for_each循環。如何調用具有for_each循環參數的函數?

這裏是我想工作的代碼示例:

#include <vector> 
#include <algorithm> 
#include <functional> 

class BaseClass 
{ 
    public: 
     virtual void Setup() = 0; 
     virtual void DisplayText(int key, int x, int y) = 0; 
}; 

class A: public BaseClass 
{ 
    public: 
     void Setup(); 
     void DisplayText(int key, int x, int y); 
}; 

class B: public BaseClass 
{ 
    public: 
     void Setup(); 
     void DisplayText(int key, int x, int y); 
}; 

void demo(A *a, B *b, std::vector<BaseClass*>& storageVector) 
{ 
    storageVector.push_back(a); 
    storageVector.push_back(b); 

    std::for_each (storageVector.begin(), storageVector.end(), 
     std::mem_fn(&BaseClass::Setup)); 

    std::for_each (storageVector.begin(), storageVector.end(), 
     std::mem_fn(&BaseClass::DisplayText)); 
} 

我從編譯器得到這些錯誤:

error C2064: term does not evaluate to a function taking 1 arguments 
error C2752: 'std::_Result_of<_Fty,_V0_t,_V1_t,_V2_t,_V3_t,_V4_t, 
    _V5_t,<unnamed-symbol>,_Obj>' : more than one partial 
    specialization matches the template argument list 

如果我試圖傳遞參數的功能,例如

for_each (storageVector.begin(), storageVector.end(), 
    std::mem_fn(&BaseClass::DisplayText(0,0,0)));) 

然後我也得到

error C2352: 'BaseClass::DisplayText' : 
    illegal call of non-static member function 

我缺少什麼?

+0

錯誤往往帶有行號。你的是否這樣做?他們對應哪條線? – 2015-02-10 16:52:08

+1

在第二個for_each循環中,DisplayText方法應接收聲明中公佈的3個參數。也許這是一個問題。 – average 2015-02-10 16:56:17

+0

@MarcusMüller它說錯誤在文件xrefwrap中。絕對是一個我沒有碰到的文件。在我的源代碼中編寫了所有這些.cpp – SyntaxIsEvil 2015-02-10 16:57:44

回答

4

函數DisplayText需要三個額外的參數,這些參數沒有提供。您需要:

變化DisplayText不要求任何參數

或者使用lambda,提供他們:

for_each(storageVector.begin(), storageVector.end(), 
    [](BaseClass* c){ c->DisplayText(key, x, y); }); 

或使用,每個爲他們提供循環:

for (auto c : storageVector) 
    c->DisplayText(key, x, y); 

或者將參數綁定到仿函數:

for_each(storageVector.begin(), storageVector.end(), 
    std::bind(std::mem_fn(&BaseClass::DisplayText), _1, key, x, y)); 
+0

該自動循環正是我需要使其工作。非常感謝你的回答! – SyntaxIsEvil 2015-02-10 17:14:22

+0

也許'std :: bind'也有竅門 – 2015-02-10 17:20:32

+0

我也增加了這種可能性 – StenSoft 2015-02-10 17:34:11

0

從cppreference.com,你的函數的簽名應該是void fun(const Type &a)。我相信問題可能是您的簽名不接受任何參數(在Setup的情況下)或多個參數(在DisplayText的情況下),這不是for_each預期的結果。

您可能需要:

void my_func(const BaseClass &r_arg) {}; 
3

可以使用lambda函數。

std::for_each (storageVector.begin(), storageVector.end(), [] (BaseClass* base) {base->Setup();}); 
std::for_each (storageVector.begin(), storageVector.end(), [] (BaseClass* base) {base->DisplayText(1, 2, 3);}); 

您可以在http://en.cppreference.com/w/cpp/language/lambda閱讀更多關於lambda函數。

+0

我不知道lambda函數。那些真的很整齊。當我嘗試添加到我的主,但它給了我這個錯誤:智能感知:一個封閉的函數局部變量不能在lambda體中引用,除非它在捕獲列表中。任何想法爲什麼它會給我這個? – SyntaxIsEvil 2015-02-10 17:18:52

+0

lambda函數通過使用語法捕獲在其封閉的範圍的一切'[=](BaseClass的*基){}'。他們還可以使用'[a,b](BaseClass * base){}'從封閉範圍捕獲特定名稱。 – 2015-02-10 17:21:51

+0

噢好吧。我只需要在方括號中添加變量。這有效。整齊。謝謝你的答案。 – SyntaxIsEvil 2015-02-10 17:27:51

1

您需要將一些參數綁定到函數DisplayText,該函數需要3個參數key,xy。有多種方法可以做到這一點。例如,你可以創建你自己的函子對象來完成綁定,或者你可以使用lambda。

最簡單的方法是將常量值綁定到參數,但也可以綁定基於某些外部信息計算的值。無論哪種方式,您都不能在不提供所有參數的情況下調用DisplayText

在你的第二個例子中,你試圖用參數「取一個函數的地址」。你不能直接在C++中做到這一點。一些其他的語言直接允許你綁定一些小於函數聲明參數實際數量的參數,這個概念被稱爲currying。在不支持這種語言的語言中,典型的方法是使用閉包來存儲附加參數的狀態,並允許稍後調用閉包。這個功能是C++ 11的lambda支持的功能。

for_each期待一個函數,它只有一個參數,但你給它不同的東西,那就是你的錯誤說什麼。 std::mem_fn接受一個成員函數並將其轉換爲一個非成員可調用對象,它接受一個額外的參數。這意味着你現在給for_each預期4個參數而不是1個參數。第一個參數是要調用的對象,另外三個參數是DisplayText預期的原始參數。

我將向您展示函子對象方法,因爲即使在舊版本的C++中,它也可以工作,並且實際上std::function和lambda對象在封面下方工作的方式。 C++ 11以更加簡潔的方式爲此提供了新的語法,但理解它的工作原理對理解新的語法很有用。

class F { 
public: 
    F(int key, int x, int y) : key(key), x(x), y(y) {} 

    void operator()(BaseClass *d) const { 
     d->DisplayText(key, x, y); 
    } 

private: 
    int key; 
    int x; 
    int y; 
}; 

for_each(storageVector.begin(), storageVector.end(), F(1, 2, 3)); 

如果你做這種方式,你會發現自己寫很多這樣的一個脫扔掉類,他們做的是存儲的一些參數給定數量的與特定類型的簽名。意識到這一點,你可以通過模擬參數的類型來概括這個類。再進一步,您可以通過獲取可變數量的模板參數來進一步推廣它,從而支持可變數量的參數。這正是std::function類型的工作原理。

相關問題