У меня есть класс с членом 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
как член класса?
c++
c++11
move-semantics
unique-ptr
huitlarc
источник
источник
std::unique_ptr<Bar, decltype(&destroy)> ptr_;
unique_ptr
(все они должны хранить указатель функции вместе с указателем на фактические данные), требует каждый раз передавать функцию уничтожения, она не может быть встроена (поскольку шаблон не может специализируются на конкретной функции, только на сигнатуре) и должны вызывать функцию через указатель (более затратный, чем прямой вызов). Оба RICi и Deduplicator игровая ответы избежать всех этих расходов, специализируясь на функтора.Это можно сделать чисто, используя лямбду в C ++ 11 (проверено в G ++ 4.8.2).
Учитывая это многоразовое использование
typedef
:Ты можешь написать:
Например, с
FILE*
:Благодаря этому вы получаете преимущества безопасной очистки с использованием RAII без необходимости использования шума try / catch.
источник
std::function
в определении и т.п.?std::function
. В отличие от этого решения, лямбда или пользовательский класс, как в принятом ответе, могут быть встроены. Но этот подход имеет преимущество в том случае, если вы хотите изолировать всю реализацию в выделенном модуле.deleted_unique_ptr<Foo> foo(new Foo(), customdeleter);
еслиcustomdeleter
следовать соглашению (он возвращает void и принимает в качестве аргумента необработанный указатель).Вам просто нужно создать класс удаления:
и укажите его в качестве аргумента шаблона
unique_ptr
. Вам все равно придется инициализировать unique_ptr в ваших конструкторах:Насколько мне известно, все популярные библиотеки C ++ это правильно реализуют; поскольку на
BarDeleter
самом деле у него нет состояния, он не должен занимать место вunique_ptr
.источник
struct BarDeleter
) tostd::unique_ptr
(std::unique_ptr<Bar, BarDeleter>
), который позволяетstd::unique_ptr
конструктору самостоятельно создавать экземпляр Deleter. т.е. разрешен следующий кодstd::unique_ptr<Bar, BarDeleter> bar[10];
typedef std::unique_ptr<Bar, BarDeleter> UniqueBarPtr
unique_ptr
, нет необходимости предоставлять экземпляр удалителя при построении) и добавляет преимущество возможности использоватьstd::unique_ptr<Bar>
где угодно без необходимости помнить использовать специальныйtypedef
или явно поставщик второго параметра шаблона. (Чтобы было ясно, это хорошее решение, я проголосовал за него, но оно останавливает на один шаг до бесшовного решения)Если вам не нужно иметь возможность изменять средство удаления во время выполнения, я настоятельно рекомендую использовать пользовательский тип удаления. Например, если использовать указатель на функцию для вашего Deleter,
sizeof(unique_ptr<T, fptr>) == 2 * sizeof(T*)
. Другими словами, половина байтовunique_ptr
тратится объекта.Однако написание специального средства удаления для обертывания каждой функции - проблема. К счастью, мы можем написать тип по шаблону для функции:
Начиная с C ++ 17:
До C ++ 17:
источник
deleter_from_fn
есть.Вы знаете, что использование специального средства удаления - не лучший способ, так как вам придется упоминать об этом во всем своем коде.
Вместо этого, поскольку вам разрешено добавлять специализации к классам уровня пространства имен в
::std
, если задействованы пользовательские типы и вы уважаете семантику, сделайте следующее:Специализируйтесь
std::default_delete
:А может быть, тоже
std::make_unique()
:источник
std
открывает совершенно новую банку червей. Также обратите внимание, что специализацияstd::make_unique
не разрешена в C ++ 20 (поэтому не должна выполняться раньше), потому что C ++ 20 запрещает специализацию вещей,std
которые не являются шаблонами классов (std::make_unique
это шаблон функции). Обратите внимание, что вы также, вероятно, получите UB, если указатель, переданный в,std::unique_ptr<Bar>
был выделен не изcreate()
, а из какой-либо другой функции распределения.std::default_delete
соответствует требованиям исходного шаблона. Я предполагаю, чтоstd::default_delete<Foo>()(p)
это был бы допустимый способ записиdelete p;
, поэтому, еслиdelete p;
бы он был допустимым для записи (то есть, если быFoo
он был завершен), это было бы не то же самое поведение. Более того, еслиdelete p;
запись была недопустимой (Foo
неполная), это будет указывать на новое поведениеstd::default_delete<Foo>
, а не сохранять его прежним.make_unique
Специализация проблематично, но я определенно использовалstd::default_delete
перегрузку (не шаблонного сenable_if
, только для C структур , как OpenSSL ,BIGNUM
которые используют известную функцию разрушения, где подклассы не случится), и это, безусловно , самый простой подход, поскольку остальную часть вашего кода можно просто использоватьunique_ptr<special_type>
без необходимости передавать тип функтора в качестве шаблонаDeleter
повсюду, а также использоватьtypedef
/using
для присвоения имени указанному типу, чтобы избежать этой проблемы.Вы можете просто использовать
std::bind
с вашей функцией уничтожения.Но, конечно, вы также можете использовать лямбду.
источник