【问题标题】:Can I create anonymous classes in C++ and capture the outside variables like in Java?我可以在 C++ 中创建匿名类并像在 Java 中一样捕获外部变量吗?
【发布时间】:2023-04-03 19:58:01
【问题描述】:

在Java中,当我需要回调函数时,我必须实现一个匿名类。在匿名类中,如果外部变量是final,我可以访问它们。

现在我在 C++ 中做同样的事情。我知道 C++ lambda 效果更好,但有时我需要传入许多函数,而对于匿名类,我只需要传入一个实例。

我尝试了以下示例。它适用于 GCC 4.3.4。

class IA {
public:
  virtual int f(int x) = 0;  
};

int main() {
    class : public IA {
        int f(int x) { return x + 1; }
    } a;
    doFancyWork(&a);
    return 0;
}

是否可以像这样捕获外部变量?

int main() {
    int y = 100; // mark y as final if possible
    class : public IA {
        int f(int x) { return x + y; }
    } a;
    return 0;
}

更新:

第二个示例无法编译。错误就在这里,

prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error:   ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’

更新:

我刚刚意识到这样做还有一些问题:

  • 我无法编写构造函数,因为该类没有名称
  • 初始化列表不允许继承。
  • 对其进行编译的任何更改都会使代码不可读。

我想我必须远离匿名课程。

【问题讨论】:

  • 是否编译运行?
  • 第一个示例编译。 (用ideone.com 测试)第二个没有。
  • 要获取y 的值,您需要将成员y 添加到您的匿名类。 (或者 lambda 会以这种方式自动处理“捕获”。)
  • 4.3.4?为什么要用古老的编译器测试你的代码?
  • 您可以将成员变量_y 添加到IA 并将其设置为y 的值,然后再将对象作为回调提供

标签: c++ c++11 lambda anonymous-class


【解决方案1】:

没有办法自动捕获这些变量,但您可以使用另一种方法。这是如果您想通过引用捕获:

int main() {
    int y = 100; // mark y as final if possible
    class IB : public IA {
    public:
      IB(int& y) : _y(y) {}
      int f(int x) { return x + _y; }
    private:
      int& _y;
    } a (y);
    return 0;
}

如果要按值捕获,只需将int&amp;更改为int即可。

无论如何,您可以考虑使用 lambdas 元组作为“多回调”对象,如果这让您对单个 lambdas 感到困扰的话。您仍然可以将所有内容打包在一个对象中,并且可以免费进行捕获。

举个例子:

auto callbacks = make_tuple(
    [] (int x) { cout << x << endl; },
    [&] () { cout << y << endl; }, // y is captured by reference
    [=] (int x) { cout << x + y << endl; }, // y is captured by value
    // other lambdas here, if you want...
    );

【讨论】:

  • +1 表示 lambda 元组。对于手头的问题,这是一个非常优雅的解决方案。
  • @JakeWoods:谢谢。我确实建议使用它,因为您不必手动捕获变量
  • 嗨,我有一个问题,我已经使用 lambda 创建了元组并添加到了 Map,但是当我迭代映射并从元组中获取第一个 lambda 并且当我调用函数时程序崩溃了.这里的 map 是字段变量,将元组放在一个函数中并返回到另一个函数中。谢谢!!
【解决方案2】:

您可以手动捕获变量(这类似于 lambda 捕获在后台执行的操作):

int main() {
    int y = 100;
    struct { 
        int& y;
        int operator()(int x) { return x + y; }
    } anon = { y };
}

然后你可以像这样使用它:

#include <iostream>
...
std::cout << anon(10) << std::endl;

按预期打印 110。不幸的是,您不能使用此方法从另一个类型继承匿名类型,因为初始化列表可构造类型不能从另一个类型继承。如果继承至关重要,那么您应该使用constructor method outlined by Andy Prowl

【讨论】:

  • 这不会编译我恐怕...需要一些改变
  • 这不能在我公司使用的编译器 gcc 4.7.1 上编译。错误如下所示,error: could not convert ‘{1}’ from ‘’ to ‘main()::
  • “如果继承至关重要” - 如果不是,并且他只是在追求一个匿名函数对象,那么确实没有必要避免使用 lambda。
【解决方案3】:

C++ lambda 可以捕获“外部”变量。 [编辑:当我第一次阅读这个问题时,我不知何故错过了他提到他知道 lambdas 的地方。无论好坏,C++ 没有任何其他真正类似于匿名类的东西]。

例如:

#include <iostream>

int main(){ 

    int y = 100;
    auto lambda = [=](int x) { return x + y; };

    std::cout << lambda(2);
}

...打印102 作为其输出。

请注意,尽管它看起来有点像一个函数,但 C++ lambda 确实会导致创建一个类。我想我应该补充一点:该类在技术上并不是匿名的,但它有一些从未直接可见的未指定名称。

编辑:尽管如此,我仍然对不使用 lambda 的理由感到有些困惑。是否打算使用一个包含许多成员函数的类?如果是这样,则不清楚您计划如何指定在哪个时间/出于何种目的调用哪个成员函数。我的直接反应是,这听起来很可疑,就好像您试图扭曲语言以支持有问题的设计一样。

【讨论】:

  • 他似乎很清楚这一点,但不想使用 lambda,因为他希望它同时封装许多不同的函数。虽然我不得不从内心深处不赞成他的方法,并且也建议只使用 lambda,但您的回答仍然没有真正回答实际问题。
  • @ChristianRau:哎呀,我错过了他提到意识到 lambdas 的地方。
  • @JerryCoffin 我需要将一个事件处理程序传递给其他人,在那里我可以实现多个函数,例如 onEvent()、onErr() 和 destroy()。因为可能有很多事件处理程序,所以我不想将它们定义为单独的类并为它们分别命名。对于 lambda 函数,似乎我必须一个一个地传递它们,或者无论如何都要将它们放入容器类中。
  • 至于您的编辑,听起来他的doFancyWork 函数知道要调用什么函数。所以它不只是使用一些模板函数类型的() 运算符,而是期望一个带有一堆方法的抽象接口,他的匿名类实现了。最后,通常的 Java 风格回调。尽管这种风格是否真的应该转移到另一种语言中仍然存在疑问,这种语言有自己惯用的(并且同样灵活)的回调方式。
【解决方案4】:

限制访问外部变量的不是类的匿名性。在问题中, y 不可访问,因为该类是在函数中本地定义的。

本地定义的类有一些限制。首先,它们只能访问静态的局部变量,但可以访问函数范围内可用的任何其他变量。此外,本地类不能有静态数据成员。

对于匿名类,你不能有构造函数也不能有析构函数。所有成员函数都必须在类定义中声明。它不能有静态静态成员,这包括通常可以在类定义中实例化的 const 静态整数成员。也不允许继承。

匿名类是 C++ 的一个不起眼的角落,几乎没有实用价值。 Lambda 函数和其他技术要灵活得多。但谁知道呢,也许在某些情况下它可以帮助提高代码的可读性。

【讨论】:

    【解决方案5】:

    如果您的IA 类确实只有一个需要覆盖的虚拟方法(真正复杂的是其他非虚拟方法),但您不想捕获该方法需要的局部变量 ,这个怎么样:

    int main() {
      int y = 100;
      auto f = [=](int x){return x+y;};
      typedef decltype(f) F;
      struct IB : IA {
        F _f;
        IB(F _f): _f(_f) {}
        int f(int x) { return _f(x); }
      } a(f);
      doFancyWork(&a);
      return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-15
      相关资源
      最近更新 更多