如何在 C++11 中模拟没有 lambda 表达式的嵌套函数?

How can I simulate a nested function without lambda expressions in C++11?

我有以下代码:

int main(int argc, char **argv)
{
    App app(800, 600);
    app.add_event_scene(Scene("Event Plot", event_plot));

    Image x("sample.png");
    struct foo { static void visual_plot() { x.draw(); } }; // Error.

    app.add_visual_scene(Scene("Visual Plot", foo::visual_plot));
    app.run();
    return 0;
}

我收到以下错误:

||=== Build: Debug in Joy-Plus-Plus (compiler: GNU GCC Compiler) ===|
G:\Development\Game-Development\CB\Joy-Plus-Plus\main.cpp|54|error: use of local variable with automatic storage from containing function|
G:\Development\Game-Development\CB\Joy-Plus-Plus\main.cpp|53|error:   'Image x' declared here|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

I'm writing a multimedia/game engine for the Allegro 5 library,我已经将主循环的绘图部分(以及事件部分)抽象为 "scene" 带有绘图(函数)的对象。每个过程都传递给应用程序,以便它在主循环中得到 "run"。问题是,"C++ approach" 不起作用:

Image x("sample.png");

void visual_plot()
{
    x.draw(); // Problem.
}

int main(int argc, char **argv)
{
    App app(800, 600);
    app.add_event_scene(Scene("Event Plot", event_plot));
    app.add_visual_scene(Scene("Visual Plot", visual_plot));
    app.run();
    return 0;
}

虽然代码运行了,但是出现了这样的情况:

如果我将 x 放在 visual_plot 中,图像将正常加载:

但现在我遇到了一个巨大的性能问题,因为在每个主循环中都会创建一个新的 Image 对象(并且很快整个事情就会冻结)。

当我把它放在函数范围之外时找不到图像,因为它必须在应用程序初始化之后,但是因为我有一个typedef函数指针将该函数作为参数的场景,我也必须给它一个void函数。问题是我无法在 C++ 中创建本地/嵌套函数(在应用程序初始化之后)。所以,为了避免这个问题,我尝试了明显的(Lambda表达式/闭包):

int main(int argc, char **argv)
{
    App app(800, 600);
    app.add_event_scene(Scene("Event Plot", event_plot));
    Image x("sample.png");
    app.add_visual_scene(Scene("Visual Plot", [&x]()->void{x.draw();}));
    app.run();
    return 0;
}

问题是Scene的构造函数的第二个参数接受了一个函数指针:

typedef void(*plot)();
typedef map<string, plot> act;

class Scene
{
private:
    string name;
    plot p;
public:
    Scene(string name, plot p);
    ~Scene() {};
    string get_name();
    plot get_plot();
    void set_name(string value);
    void set_plot(plot value);
};

并且由于函数不能作为参数传递,并且会衰减为指针,这同样适用于 lambda 表达式(它不是函数),所以我得到以下错误:

G:\Development\Game-Development\CB\Joy-Plus-Plus\main.cpp|52|error: no matching function for call to 'Scene::Scene(const char [12], main(int, char**)::__lambda0)'|

面对这样的悲剧,我该如何在C++11中模拟一个嵌套函数呢? Since simulating like this answer 不起作用。

OBS:我同意这可能是设计问题,但我几乎不这么认为。对我来说,C++ 就是不想让我以任何方式将那个该死的函数作为参数传递(所以,我请求各位 C++ 高手的帮助)。

只需将图像放入 visual_plot 函数并使其静态化即可:

void visual_plot()
{
    static Image img("sample.png");
    x.draw(); // Problem.
}

这将在第一次调用 visual_plot 时初始化 img,并且仅在那时。这将同时解决性能问题和 "it must be initialized after app.run()" 问题。

这是一个设计问题。为了完成您正在尝试做的事情,您需要两条信息:要执行的代码和要执行的数据。

lambda 并不神奇,它只是将这两者封装到一个对象中,这就是为什么它不能很好地退化为单个函数指针。带捕获的 lambda 是函数对象的语法糖:

int x, y;
float f;

// ...

auto l = [x, &y, f] () { return static_cast<int>((x + y) * f); };
int r = l();

让您免于写作

struct Values {
    int x;
    int& y;
    float f;

    int operator() () {
        return static_cast<int>((x + y) * f);
    }

    Capture(int x_, int& y_, float f_) : x(x_), y(y_), f(f_) {}
};

//...

Capture c(x, y, f);

int r = c();

最后是一个成员函数调用,所以涉及两个指针:一个指向成员函数的指针 'operator()' 和一个指向调用它的实例的指针。

int r = Capture::operator=(&c); // pseudo

使用静态或全局变量,您可以在编译时知道数据的地址,这样您就只需要一个函数指针。

但是您的设计是只接受一个参数的 strcpy 或接受一个参数的打印函数 none:您怎么知道要复制或打印什么?

更好的设计要么让您将函数对象传递给绘图函数,看看 STL 谓词如何工作,这将允许函数指针和 lambda,要么使用虚函数和子类化。

struct Scene { virtual void plot(); };
struct MyScene : public Scene {
    Image x;
    MyScene() : x("image") {}
    void plot() override { x.draw(); }
};

这种方法的缺陷是“slicing”,如果允许派生类型,则需要通过引用而不是值传递 Scene

void foo(Scene& s) {
    s.plot();
}

foo(MyScene(...)); // not going to go well