使用lambda在功能指针铸造时双免费



花了很多天在复杂的程序中调试一个非常晦涩的错误后,我终于将问题减少到了一个非常简单,可重复的情况。

基本上,如果我有一个简单的非捕捉lambda,带有一个签名,该签名按值按值进行单个参数(并按值返回对象(,并且我将该lambda转换为等效函数指针类型,然后调用函数指针,对象的驱动器作为参数传递,被错误地调用。

这是一个简单的可复制案例,显示了问题,并使用GCC 4.9.2:

引起双重免费。
#include <iostream>
#include <memory>
#include <utility>
#include <cassert>
struct Foo
{
    Foo() = default;
    Foo(const Foo&) = delete;
    Foo& operator = (const Foo&) = delete;
    ~Foo()
    {
        std::cout << "Destroying object " << this << std::endl;
    }
};
auto callback = [](std::unique_ptr<Foo> p)
{
    assert(p);
    return p;
};
int main()
{
    std::unique_ptr<Foo> ptr(new Foo());
    auto fptr = static_cast<std::unique_ptr<Foo>(*)(std::unique_ptr<Foo>)>(callback);
    auto result = fptr(std::move(ptr));
}

该程序的预期行为是分配由unique_ptr管理的Foo的唯一实例。然后通过将其作为参数传递给callback来移动该实例,然后在callback返回值返回时再次移动实例,最后破坏实例。(授予编译器还可以使用复制仪式删除至少一个移动构造函数,但这并不重要。(

因此,我真的应该只看到Foo的destructor一次。

相反,我在运行此程序时看到了这一点:

Destroying object 0xdf4410
Destroying object 0xdf4410
*** Error in `./test9': double free or corruption (fasttop): 0x0000000000df4410 ***

以某种方式,编译器正在生成代码,该代码在传递给callback时会错误调用 unique_ptr destructor。

请注意,如果我将非捕捉lambda转换为等效函数指针,则只会发生这种。如果我直接调用callback,那没有问题。

所以我看不出为什么会发生这种情况,因此,我问这是否可能是lambda的编译器错误,以便在此功能指针转换,或者我在这里只是做错了什么。

根据Godbolt的测试,直到GCC6.1才固定。2个不同版本的编译器的函数lambda :: _有趣的编译器自动生成的娱乐:_娱乐之间存在明显的区别。该功能用于调用lambda :: operator((。

for gcc4.9
lambda::_FUN(unique_ptr<Foo> ptr)
    tmp = ptr //copy constructor is called by compiler, although it's already declared as "delete". it means there are 2 owners for same Foo object.
    lambda::operator(tmp)
for gcc 6.1 or higher
lambda::_FUN(unique_ptr<Foo> ptr)
    lambda::operator(ptr)

最新更新