我可以在C ++中创建匿名类并捕获像Java中的外部变量吗?

时间:2013-01-16 21:57:29

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

在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’

更新:

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

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

我想我必须放弃匿名课程。

5 个答案:

答案 0 :(得分:36)

无法自动捕获这些变量,但您可以使用其他方法。如果您想通过引用捕获:

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&更改为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 :(得分:6)

您可以手动捕获变量(类似于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

答案 2 :(得分:5)

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确实会导致创建一个类。我想我应该补充一点:该类在技术上并不是匿名的,但它有一些从未直接可见的未指定名称。

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

答案 3 :(得分:2)

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

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

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

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

答案 4 :(得分:1)

如果你的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;
}