在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’
更新:
我刚刚意识到这样做的一些问题:
我想我必须放弃匿名课程。
答案 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;
}