IT story

std :: unique_ptr 멤버와 함께 사용자 정의 삭제기를 사용하려면 어떻게합니까?

hot-time 2020. 7. 11. 09:35
반응형

std :: unique_ptr 멤버와 함께 사용자 정의 삭제기를 사용하려면 어떻게합니까?


unique_ptr 멤버가있는 클래스가 있습니다.

class Foo {
private:
    std::unique_ptr<Bar> bar;
    ...
};

Bar는 create () 함수와 destroy () 함수가있는 타사 클래스입니다.

std::unique_ptr독립형 기능 으로 함께 사용하고 싶다면 다음을 수행 할 수 있습니다.

void foo() {
    std::unique_ptr<Bar, void(*)(Bar*)> bar(create(), [](Bar* b){ destroy(b); });
    ...
}

std::unique_ptr수업의 일원으로서 이것을 할 수있는 방법이 있습니까?


그 가정 create하고 destroy다음 서명을 (영업 이익의 코드의 경우 것으로 보인다) 무료 기능입니다 :

Bar* create();
void destroy(Bar*);

Foo이런 식으로 수업을 쓸 수 있습니다

class Foo {

    std::unique_ptr<Bar, void(*)(Bar*)> ptr_;

    // ...

public:

    Foo() : ptr_(create(), destroy) { /* ... */ }

    // ...
};

destroy이미 삭제 도구 이므로 여기에 람다 또는 사용자 지정 삭제 도구를 작성할 필요가 없습니다 .


C ++ 11 (G ++ 4.8.2에서 테스트)의 람다를 사용 하여이 작업을 깨끗하게 수행 할 수 있습니다.

이 재사용이 가능하면 typedef:

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

당신은 쓸 수 있습니다:

deleted_unique_ptr<Foo> foo(new Foo(), [](Foo* f) { customdeleter(f); });

예를 들어 FILE*:

deleted_unique_ptr<FILE> file(
    fopen("file.txt", "r"),
    [](FILE* f) { fclose(f); });

이를 통해 try / catch 노이즈없이 RAII를 사용하여 예외 안전 클린업의 이점을 얻을 수 있습니다.


삭제 클래스를 작성하면됩니다.

struct BarDeleter {
  void operator()(Bar* b) { destroy(b); }
};

의 템플릿 인수로 제공하십시오 unique_ptr. 여전히 생성자에서 unique_ptr을 초기화해야합니다.

class Foo {
  public:
    Foo() : bar(create()), ... { ... }

  private:
    std::unique_ptr<Bar, BarDeleter> bar;
    ...
};

내가 아는 한, 모든 인기있는 c ++ 라이브러리는 이것을 올바르게 구현합니다. 이후 BarDeleter실제로 어떤 상태가없는, 그것은 어떤 공간을 점유 할 필요가 없습니다 unique_ptr.


런타임에 삭제기를 변경할 수 없다면 사용자 정의 삭제 기 유형을 사용하는 것이 좋습니다. 예를 들어, Deleter가에 대한 함수 포인터를 사용하는 경우 sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*). 즉, unique_ptr객체 바이트의 절반 이 낭비됩니다.

Writing a custom deleter to wrap every function is a bother, though. Thankfully, we can write a type templated on the function:

Since C++17:

template <auto fn>
using deleter_from_fn = std::integral_constant<decltype(fn), fn>;

template <typename T, auto fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;

// usage:
my_unique_ptr<Bar, destroy> p{create()};

Prior to C++17:

template <typename D, D fn>
using deleter_from_fn = std::integral_constant<D, fn>;

template <typename T, typename D, D fn>
using my_unique_ptr = std::unique_ptr<T, deleter_from_fn<D, fn>>;

// usage:
my_unique_ptr<Bar, decltype(destroy), destroy> p{create()};

You can simply use std::bind with a your destroy function.

std::unique_ptr<Bar, std::function<void(Bar*)>> bar(create(), std::bind(&destroy,
    std::placeholders::_1));

But of course you can also use a lambda.

std::unique_ptr<Bar, std::function<void(Bar*)>> ptr(create(), [](Bar* b){ destroy(b);});

You know, using a custom deleter isn't the best way to go, as you will have to mention it all over your code.
Instead, as you are allowed to add specializations to namespace-level classes in ::std as long as custom types are involved and you respect the semantics, do that:

Specialize std::default_delete:

template <>
struct ::std::default_delete<Bar> {
    default_delete() = default;
    template <class U, class = std::enable_if_t<std::is_convertible<U*, Bar*>()>>
    constexpr default_delete(default_delete<U>) noexcept {}
    void operator()(Bar* p) const noexcept { destroy(p); }
};

And maybe also do std::make_unique():

template <>
inline ::std::unique_ptr<Bar> ::std::make_unique<Bar>() {
    auto p = create();
    if (!p) throw std::runtime_error("Could not `create()` a new `Bar`.");
    return { p };
}

참고URL : https://stackoverflow.com/questions/19053351/how-do-i-use-a-custom-deleter-with-a-stdunique-ptr-member

반응형