如何调用具有来自 for_each 循环的参数的函数?

How do I call a function that has arguments from a for_each loop?

我有一个 C++ 程序将对象存储在一个向量中,然后使用 std::for_each 对每个对象调用一个函数。如果被调用函数需要带参数,我不明白如何编写 std::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

我错过了什么?

从 cppreference.com 开始,您的函数的签名应该是 void fun(const Type &a)。我认为问题可能是您的签名不接受任何参数(在 Setup 的情况下)或多个参数(在 DisplayText 的情况下),这不是 for_each 是什么期待。

您可能需要:

void my_func(const BaseClass &r_arg) {};

您可以使用 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 函数的信息。

函数 DisplayText 需要三个未提供的附加参数。您需要:

更改DisplayText不需要任何参数

或者使用提供它们的 lambda:

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

或者使用提供它们的 for-each 循环:

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));

您需要将一些参数绑定到函数 DisplayText,它有 3 个参数 keyxy。有多种方法可以做到这一点。例如,您可以创建自己的执行绑定的仿函数对象,或者您可以使用 lambda。

最简单的方法是将常量值绑定到参数,但您也可以绑定根据某些外部信息计算的值。无论哪种方式,您都不能在不提供所有参数的情况下调用 DisplayText

在您的第二个示例中,您正在尝试 "take the address" 带有参数的函数。你不能直接在 C++ 中这样做。其他一些语言直接允许你绑定一些少于函数实际声明参数数量的参数,这个概念被称为柯里化。在不支持这一点的语言中,典型的方法是使用闭包来存储附加参数的状态并允许稍后调用闭包。此功能是 C++11 的 lambda 所支持的。

for_each 期望一个函数只接受一个参数,但你给它一些不同的东西,这就是你的错误所说的。 std::mem_fn 接受一个成员函数并将其转换为一个接受额外参数的非成员可调用对象。这意味着你现在给 for_each 一些东西,它需要 4 个参数而不是 1 个参数。第一个参数是要调用的对象,其他 3 个参数是 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));

如果你这样做,你会发现自己写了很多这样的东西 class它们所做的只是用特定的类型签名存储一些给定数量的参数。意识到这一点,您可以通过模板化参数类型来概括此 class。更进一步,您可以通过采用可变数量的模板参数并因此支持可变数量的参数来进一步概括它。这正是 std::function 类型的工作原理。