我正在将一个可移动的对象传递给一个lambda-expression-cast-to-a-function-pointer,并且它看起来沿着该行的某个位置,该程序对该对象进行按位复制,而不调用该副本或移动构造函数。这会导致资源被双重释放,因为对象的“真实”副本不知道它已被移出。
更详细的跟踪是在MCVE代码之后。
MCVE:
// Compiled and run with:
// g++ -std=c++11 -ggdb bugtest.cpp -o bugtest && ./bugtest
#include <memory>
#include <vector>
#include <iostream>
using namespace std;
struct movable
{
vector<string> values;
movable();
~movable();
movable(movable &&);
movable& operator=(movable &&);
movable(const movable &) = delete;
movable& operator=(const movable &) = delete;
};
movable::movable() {cout << this << " default-constructing" << endl;}
movable::~movable()
{
cout << this << " destructing with " << values.size() << " values:" << endl;
for(size_t k = 0; k < values.size(); k++)
cout << " " << values[k] << endl;
}
movable::movable(movable &&o)
{
cout << this << " move-constructing with " << o.values.size() << " values from " << &o << endl;
this->values = move(o.values);
o.values.resize(0);
}
movable& movable::operator=(movable &&o)
{
cout << this << " move-assigning " << o.values.size() << " values from " << &o << endl;
this->values = move(o.values);
o.values.resize(0);
return *this;
}
#if 1
movable (*lambdaFunc)(movable) = +[](movable m) -> movable {
cout << &m << " used in lambda" << endl;
return m;
};
#else
movable lambdaFuncImpl(movable m) {
cout << &m << " used in global function" << endl;
return m;
};
movable (*lambdaFunc)(movable) = lambdaFuncImpl;
#endif
struct task
{
movable arg;
movable result;
void run()
{
result = lambdaFunc(move(arg));
}
};
int main()
{
task t;
t.arg.values.emplace_back("TheValue");
t.run();
}
G ++版:
$ g++ --version
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4
输出:
0x7fff57dcbe20 default-constructing <-- t.arg
0x7fff57dcbe38 default-constructing <-- t.result
0x7fff57dcbdc0 move-constructing with 1 values from 0x7fff57dcbe20
^-- argument to lambdaFunc
0x7fff57dcbd70 used in lambda <-- HUH!? That's not the address of an object that was constructed!
It seems to be a bitwise copy of 0x7fff57dcbdc0
0x7fff57dcbde0 move-constructing with 1 values from 0x7fff57dcbd70
<-- return value; when we move out of ...70, this doesn't affect the real object ...c0
0x7fff57dcbe38 move-assigning 1 values from 0x7fff57dcbde0
^-- assigning to t.result
0x7fff57dcbde0 destructing with 0 values:
0x7fff57dcbdc0 destructing with 1 values:
TheValue
^-- Uh oh, because of all the move assignments, ...c0 and ...38 are pointing to the same memory, which is now free
0x7fff57dcbe38 destructing with 1 values
Segmentation fault (core dumped) <-- trying to print a string that is now freed
通过避免使用lambda,将#if 1
更改为#if 0
会产生预期的输出:
0x7ffe3b11b910 default-constructing <-- t.arg
0x7ffe3b11b928 default-constructing <-- t.result
0x7ffe3b11b8b0 move-constructing with 1 values from 0x7ffe3b11b910
0x7ffe3b11b8b0 used in global function<-- The same object that was just constructed
0x7ffe3b11b8d0 move-constructing with 1 values from 0x7ffe3b11b8b0
0x7ffe3b11b928 move-assigning 1 values from 0x7ffe3b11b8d0
0x7ffe3b11b8d0 destructing with 0 values:
^-- The values are not freed because this object was emptied when it was moved from.
0x7ffe3b11b8b0 destructing with 0 values:
0x7ffe3b11b928 destructing with 1 values:
TheValue
0x7ffe3b11b910 destructing with 0 values:
这个程序有什么问题?