了解 C++0x lambda 捕获
在最近的一份 C++0x 草案 (n3225.pdf) 中,我们可以找到 5.1.2/10:
使用非限定名称查找的常用规则(3.4.1)来查找捕获列表中的标识符;每个此类查找都应找到在本地 lambda 表达式的到达范围中声明的具有自动存储持续时间的变量。如果一个实体(即变量或 this)出现在 lambda 表达式的捕获列表中,则称该实体已被显式捕获。
这对我来说似乎相当有限制。例如,在我看来,以下情况是不允许的:
int global;
struct s {
int x;
void memfun() {
[x,global]{};
}
};
因为 x
不一定是具有自动存储功能的变量,也不是 global
。请注意,此捕获子句的目的是让 lambda 对象存储 x
和 global
的副本,这可能是需要的,以防万一它们是到了后期才改变。我已经知道一种替代方案:
int global;
struct s {
int x;
void memfun() {
int copyx = x;
int copyglobal = global;
[copyx,copyglobal]{};
}
};
但这归结为额外的副本和额外的样板只是为了捕获 x 和 global 作为副本。
另外,我在最新的草案中找不到任何关于如果我们在 capture 子句中命名本地引用会发生什么的结论:
int main() {
int i = 0;
int &r = i;
assert([r]{return &r;}() != &i);
}
lambda 对象是“复制引用”还是“复制 int”?如果它通过副本捕获引用的对象,这可以为我们节省之前解决方法中的额外副本。
GCC 显然支持所有这些示例,并在最后一种情况下存储 int 的副本(恕我直言,这是可取的)。但我想知道这实际上是否是 C++0x 草案的预期行为,或者只是编译器扩展或实现错误。
编辑:
templatetypedef 指出 5.1.2/14 解释了在捕获子句中命名引用时会发生什么。据我所知,这允许我们对第一个示例使用以下解决方法:
int global;
struct s {
int x;
void memfun() {
auto& cx = x;
auto& cglob = global;
[cx,cglob]{};
}
};
Tia, 塞利比策
In one of the recent C++0x drafts (n3225.pdf) we can find 5.1.2/10:
The identifiers in a capture-list are looked up using the usual rules for unqualified name lookup (3.4.1); each such lookup shall find a variable with automatic storage duration declared in the reaching scope of the local lambda expression. An entity (i.e. a variable or this) is said to be explicitly captured if it appears in the lambda-expression’s capture-list.
That seems rather restrictive to me. For example, it seems to me that the following things are disallowed:
int global;
struct s {
int x;
void memfun() {
[x,global]{};
}
};
since x
is not necessarily a variable with automatic storage and neither is global
. Note that the intention of this capture clause is to let the lambda object store a copy of x
and global
which might be desirable in case they are changed at a later stage. I am already aware of an alternative:
int global;
struct s {
int x;
void memfun() {
int copyx = x;
int copyglobal = global;
[copyx,copyglobal]{};
}
};
But this boils down to extra copies and additional boiler plate just to capture x
and global
as copy.
Also, i cannot find anything conclusive in the latest drafts about what happens if we name a local reference in the capture clause:
int main() {
int i = 0;
int &r = i;
assert([r]{return &r;}() != &i);
}
Does the lambda object "copy a reference" or "copy an int"? If it captures the referred object by copy, this can save us the additional copies from the previous work-around.
GCC apparently supports all these examples and stores a copy of an int in the last case (which is desirable, IMHO). But I would like to know whether this is in fact the intended behaviour according to the C++0x drafts or just a compiler-extension respectivly an implementatin bug.
Edit:
templatetypedef pointed out 5.1.2/14 which explains what happens when a reference is named in a capture-clause. As far as I can tell, this allows us to use the following work-around for the first example:
int global;
struct s {
int x;
void memfun() {
auto& cx = x;
auto& cglob = global;
[cx,cglob]{};
}
};
Tia,
sellibitze
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
从您发布的内容来看,您的第一个示例似乎是非法的,因为捕获的变量都没有自动持续时间。
但是,您可以轻松解决此问题。要捕获数据成员,您只需捕获它即可,不需要捕获全局,因为您可以直接引用它。编辑:正如您所指出的,这将不要创建要捕获的值的本地副本。
要在创建副本时捕获这些变量,您可以捕获它,然后在 lambda 内部显式创建数据成员的本地副本。至于关于捕获引用的第二个问题,§5.1.2/ 14 表示通过复制捕获引用类型的变量将创建引用值的副本,而不是创建引用的副本。因此,lambda 将拥有其自己的引用创建时所引用的值的副本。
From what you've posted it seems like your first example is illegal since neither captured variable has automatic duration.
However, you can easily fix this. To capture the data member, you can just capture this, and the global doesn't need to be captured as you can just reference it directly.EDIT: As you pointed out, this will not create a local copy of the value you want to capture.
To capture these variables while making a copy, you can capture this, then explicitly create a local copy of the data member inside of the lambda.As for the second question about capturing references, §5.1.2/14 says that capturing a variable of reference type by copy will create a copy of the value referenced instead of creating a copy of the reference. Thus the lambda will have its own copy of the value that the reference was referencing when it was created.