在C#中使用lambda表达式或匿名方法时,我们必须警惕对修改后的闭包陷阱的访问.例如:
foreach (var s in strings)
{
query = query.Where(i => i.Prop == s); // access to modified closure
...
}
Run Code Online (Sandbox Code Playgroud)
由于修改后的闭包,上面的代码将导致Where查询中的所有子句都基于最终值s.
正如这里所解释的那样,这是因为上面循环中s声明的变量foreach在编译器中被翻译成这样:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
Run Code Online (Sandbox Code Playgroud)
而不是像这样:
while (enumerator.MoveNext())
{
string s;
s = enumerator.Current;
...
}
Run Code Online (Sandbox Code Playgroud)
正如这里所指出的,在循环外声明变量没有性能优势,在正常情况下,我能想到这样做的唯一原因是你计划在循环范围之外使用变量:
string s;
while (enumerator.MoveNext())
{
s = enumerator.Current;
...
}
var finalString = s;
Run Code Online (Sandbox Code Playgroud)
但是,foreach循环中定义的变量不能在循环外使用: …
我已经看到关于变量捕获如何为变量创建闭包的无数帖子,但是它们似乎都没有具体细节,并且把整个事情称为"编译魔术".
我正在寻找一个明确的解释:
我倾向于根据值和指针(更接近内部发生的核心)的答案,尽管我会接受一个涉及值和引用的明确答案.
我知道一般来说,基于范围的for循环中临时的生命周期延伸到整个循环(我读过C++ 11:基于范围的语句:"range-init"生命周期?).所以做这样的事情一般都可以:
for (auto &thingy : func_that_returns_eg_a_vector())
std::cout << thingy;
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试做一些我认为与Qt QList容器类似的事情时,我对内存问题感到磕磕绊:
#include <iostream>
#include <QList>
int main() {
for (auto i : QList<int>{} << 1 << 2 << 3)
std::cout << i << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里的问题是valgrind在QList类中的某个地方显示无效的内存访问.但是,修改示例以便将列表存储在变量中可以提供正确的结果:
#include <iostream>
#include <QList>
int main() {
auto things = QList<int>{} << 1 << 2 << 3;
for (auto i : things)
std::cout << i << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在我的问题是:我在第一种情况下做了一些愚蠢的事情,导致例如未定义的行为(我没有足够的经验阅读C++标准以便为自己回答这个问题)?或者这是我如何使用QList或如何QList …