#c #arrays #c 11 #overloading #unique-ptr
#c #массивы #c 11 #перегрузка #уникальный-ptr
Вопрос:
В соответствии с http://en.cppreference.com/w/cpp/memory/unique_ptr/reset,
void reset( pointer ptr = pointer() );
template< class U >
void reset( U ) = delete;
void reset( std::nullptr_t p );
1) Учитывая
current_ptr
, что указатель, которым управлял*this
, выполняет
следующие действия в этом порядке:
Сохраняет копию текущего указателяold_ptr = current_ptr
;
Перезаписывает текущий указатель аргументомcurrent_ptr = ptr
;
Если старый указатель не был пустым, удаляется ранее управляемый объектif(old_ptr != nullptr) get_deleter()(old_ptr)
.2) В специализации для динамических массивов
std::unique_ptr<T[]>
этот элемент шаблона предусмотрен для предотвращения использованияreset()
с указателем на производный (что приведет к неопределенному поведению с массивами).3) В специализации для динамических массивов
std::unique_ptr<T[]>
, третья перегрузка необходима, чтобы разрешить сбросnullptr
(который в противном случае был бы запрещен перегрузкой шаблона). Эквивалентноreset(pointer())
Теперь это reset(nullptr)
эквивалентно тому reset(pointer())
, почему последнее существует?
Если я хочу сбросить массив формы unique_ptr, почему я не могу просто использовать rest(pointer())
?
Комментарии:
1. Я на самом деле нахожу метод
delete
ion более интересным…2. Потому что было бы глупо иметь
p.reset(nullptr)
ошибку компиляции?3. Обратите внимание, что
std::unique_ptr<T[]>
принимаетT *
иT *
только как тип указателя для его конструктора и дляreset
метода. Примечательно, что он не принимает указатели на типы, отличные отT
, даже если они преобразуются в указатели наT
. Другими словами: без этой перегрузкиp.reset(nullptr);
не будет компилироваться. Это было бы довольно глупо. (По той же причине естьunique_ptr(nullptr_t)
ctor .)
Ответ №1:
The
template< class U >
void reset( U ) = delete;
был бы выбран для вызова с nullptr
аргументом, если бы не
void reset( std::nullptr_t p );
Вот почему он существует, чтобы разрешить вызов with nullptr
.
Пример (компиляция с FIX
определенным для подавления ошибки компиляции):
#include <cstddef> // std::nullptr_t
struct S
{
void reset( char* ) {}
template< class Type >
void reset( Type ) = delete;
#if FIX
void reset( std::nullptr_t ) {}
#endif
};
auto main() -> int
{
S().reset( nullptr ); // Fails when FIX is not defined.
}
Комментарии:
1. Почему reset(nullptr) не может быть преобразован в reset(указатель)? Форма № 1 более конкретна, чем форма № 2. Так что, я думаю, даже если нет формы № 3, разрешение перегрузки все равно может работать.
2. @xmllmx: шаблон точно соответствует типу, в то время как не-шаблон требует преобразования.
Ответ №2:
reset
это реализовано как
pointer old = this->ptr;
this->ptr= newPointer;
delete[] old;
Шаблонная перегрузка удаляется для массивов, чтобы предотвратить следующий случай
class foo{};
class bar : public foo {};
foo* managedPointer = new foo[3];
bar* newPointer = new bar[5];
foo* old = managedPointer;
managedPointer = newPointer;
delete[] old;
Что является неопределенным поведением. Раздел 5.3.5 параграф 3:
[…] Во второй альтернативе (удалить массив), если динамический тип удаляемого объекта отличается от его статического типа, поведение не определено.
Поскольку удаленные функции по-прежнему участвуют в разрешении перегрузки и reset(U)
обеспечивают лучшее соответствие для nullptr
than reset(pointer)
, существует дополнительная перегрузка для разрешения reset(nullptr)
, что в противном случае привело бы к ошибке компилятора и, следовательно, привело бы к несогласованному интерфейсу между версией массива и указателя.