Двойное освобождение при использовании лямбда-функции для приведения указателя - PullRequest
2 голосов
/ 24 июня 2019

Потратив много дней на отладку очень непонятной ошибки в сложной программе, я, наконец, свел проблему к очень простому, воспроизводимому сценарию.

По сути, получается, что если у меня есть простая не захватывающая лямбда с сигнатурой, которая принимает один аргумент по значению (и возвращает объект по значению), и я преобразую эту лямбду в эквивалентный тип указателя на функцию, а затем вызываю указатель на функцию, деструктор объекта, переданного в качестве аргумента, вызывается неправильно.

Вот простой воспроизводимый случай, который показывает проблему и вызывает двойное освобождение с помощью 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));
}

Предполагаемое поведение этой программы - выделить уникальный экземпляр Foo, управляемый unique_ptr. Затем переместите этот экземпляр один раз, передав его в качестве аргумента callback, затем снова переместите экземпляр, когда callback вернет его по значению, и, наконец, уничтожьте экземпляр. (Разумеется, компилятор может также удалить хотя бы один из вызовов конструктора перемещения с использованием copy-elision, но здесь это не важно.)

Поэтому я действительно должен увидеть деструктор Foo только один раз.

Вместо этого я вижу это при запуске этой программы:

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

Почему-то компилятор генерирует код, который некорректно вызывает деструктор unique_ptr при его передаче callback.

Обратите внимание, что это только происходит, если я преобразую не захватывающую лямбду в эквивалентный указатель на функцию. Если я вызову callback напрямую, проблем не будет.

Так что я не понимаю, почему это произошло, и поэтому я спрашиваю, не является ли это ошибкой компилятора с преобразованием лямбда-функции в указатель, или я просто что-то здесь не так делаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...