2015-02-10 140 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类型的工作原理。