Как использовать лямбда в качестве Делетера std::unique ptr?

Проверьте следующую надуманную программу:

#include <functional>
#include <memory>

template<typename T>
using UniPtr = std::unique_ptr<T, std::function<void(T*)>>;

int* alloc()
{
    return new int;
}

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

int main()
{
    auto p = func();
    return 0;
}

С std:: конструктор функций manual, я думаю, что строительство std::function объект может выбросить исключение, даже соотношение очень низкое:

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};

    return UniPtr<int>{alloc(), dealloc};
}

но если использовать указатель функции вместо

3 ответов


Я думаю, после ухода func() область,dealloc объект должен быть освобожден, и на него нельзя ссылаться.

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

значение, возвращаемое этой функцией преобразования является указателем на функцию с языка C++ связь, которая при вызове имеет тот же эффект, что и вызов оператора вызова функции объекта закрытия напрямую.


если вы определили UniPtr as

template<typename T>
using UniPtr = std::unique_ptr<T, void(*)(T*)>;

тогда следующий код действителен, нет никаких проблем о времени жизни deleter

UniPtr<int> func()
{
    auto dealloc = [](int* p){delete p;};
    return UniPtr<int>{alloc(), dealloc};
}

Цитата N3337,expr.подтянутый.лямда/6

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

таким образом, ваш deleter инициализируется указателем на функцию, которая остается действительной даже после возвращения из func.


небольшое дополнение для реализации к предыдущим ответам...

template<typename T, typename D>
std::unique_ptr<T, D> make_unique_with_deleter(T* t, D d)
{
    // granted copy elison since C++17
    return std::unique_ptr<T, D> (t, d);
}

использование:

class A
{
};

auto up1 = make_unique_with_deleter(new A, [](A* a) {delete a; });
auto up2 = make_unique_with_deleter(std::fopen("something", "w"), std::fclose);

{
    int any_value = 0;
    // caution: only local use with lambda-capture, but possible
    auto up3 = make_unique_with_deleter(new A, [any_value](A* a) {delete a; });
}

немного быстрое решение. Он работает в разных сценариях. Это позволяет избежать использования на std: function с его небольшими, но ненужными накладными расходами.