今天我遇到了一个非常奇怪的错误。我创建了一个最小的例子:
https://gist.github.com/SuperV1234/5792381
基本上,在某些机器上,“测试2”段错误;在其他方面,它按预期工作。 在我的桌面计算机上,它可以在Windows 8 x64和Linux Mint 15 x64上运行。 在我的笔记本电脑上,它是在Windows 8 x64和Linux Mint 15 x64上的段错误。
令我困惑的是:
这是编译器错误吗?或者Game::test1()
和lambda身体之间有区别吗?
// Test 1 works
// Test 2 segfaults... on some machines.
// Compiled with -std=c++11, GCC 4.8.1, tested both on native Linux, Windows and Wine
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
struct Command
{
function<void()> func;
void reset() { }
};
struct Timeline
{
vector<Command*> commands;
void clear()
{
for(auto& c : commands) delete c;
commands.clear();
}
void reset() { for(auto& c : commands) c->reset(); }
};
struct Game
{
Timeline timeline;
void test1() { timeline.clear(); timeline.reset(); }
void run()
{
{
cout << "Starting test 1..." << endl;
Command* cmd{new Command};
cmd->func = [&]{ test1(); };
timeline.commands.push_back(cmd); cmd->func();
cout << "Successfully ending test 1..." << endl;
}
{
cout << "Starting test 2..." << endl;
Command* cmd{new Command};
cmd->func = [&]{ timeline.clear(); timeline.reset(); };
timeline.commands.push_back(cmd); cmd->func();
cout << "Successfully ending test 2..." << endl;
}
}
};
int main() { Game{}.run(); return 0; }
此处提供了真实代码(不是最小示例):https://github.com/SuperV1234/SSVOpenHexagon/commit/77784ae142768f964666afacfeed74300501ec07
从真实代码中回溯:http://paste2.org/W7yeCxOO
答案 0 :(得分:4)
如果你看看反汇编,第一个lambda看起来像这样:
test1();
mov eax,dword ptr [this]
mov ecx,dword ptr [eax]
call Game::test1 (021717h)
前两行获取捕获的Game
对象的地址,并将其传递给Game::test1
。
第二个lambda看起来像这样:
timeline.clear();
mov eax,dword ptr [this]
mov ecx,dword ptr [eax]
call Timeline::clear (08415D2h)
timeline.reset();
mov eax,dword ptr [this]
mov ecx,dword ptr [eax]
call Timeline::reset (08416D6h)
这里的问题是在timeline.clear
之后lambda被破坏,第二次尝试获取捕获的Game
对象会在ecx
中放入一些垃圾。因此,使用无效指针调用Timeline::reset
。
编辑:你的lambdas基本上是这样的:
struct lambda_1 {
Game* game;
void operator()() {
game->test1();
}
};
struct lambda_2 {
Game* game;
void operator()() {
game->timeline.clear();
game->timeline.reset();
}
};
所以会发生的是你正在尝试访问已删除对象的成员。
答案 1 :(得分:3)
您正在运行lambda时删除它。我不认为这是理智的事情。
您的代码与此相当:
Command *c = new Command;
c->func = [&] { delete c; };
c->fun();
如果您确实需要这样做,可以在调用之前复制该函数:
Command *c = new Command;
c->func = [&] { delete c; };
auto f = c->func; //copy the function
f(); //c->func is deleted, but f is not!
PS:你知道你的clear
/ reset
这个东西没什么意义,不是吗?