为什么GCC在不调用move或copy构造函数的情况下复制此对象?

时间:2016-10-10 02:13:27

标签: c++ c++11 lambda

我正在将一个可移动的对象传递给一个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:

这个程序有什么问题?

0 个答案:

没有答案