Почему shared_ptr не является специализированным?

#c #pointers

#c #указатели

Вопрос:

shared_ptr<void> особенность в том, что он, по определению, будет вызывать неопределенное поведение при вызове delete a void* .

Итак, почему нет shared_ptr<void> специализации, которая выдает ошибку компиляции?

Комментарии:

1. Зачем нужна специализация? Разве это уже не выдает ошибку компилятора?

2. @MooingDuck Действительно невозможно объяснить, почему delete (void*)0; это было разрешено комитетом C . Или, может быть, это была шутка, которую разработчики восприняли всерьез.

3. Как ты думаешь, почему это невозможно, @Curiousguy? Это не значит, что все члены комитета мертвы и не оставили заметок. Я уверен, что большинство из них все еще живы, и, вероятно, есть протоколы собраний и переписка. Это не древняя история. Кто-то, желающий приложить усилия для его исследования, вероятно, мог бы точно сказать, почему это так.

Ответ №1:

Использование shared_ptr для хранения произвольного объекта

shared_ptr может действовать как универсальный указатель на объект, аналогичный void* . Когда экземпляр shared_ptr создается как:

 shared_ptr<void> pv(new X);
 

уничтожается, он правильно удалит объект X, выполнив ~X.

Это свойство можно использовать почти так же, как необработанный void* используется для временного удаления информации о типе из указателя объекта. shared_ptr позже может быть возвращен к правильному типу с помощью static_pointer_cast .

Но как?

Этот конструктор был изменен на шаблон, чтобы запомнить фактический переданный тип указателя. Деструктор вызовет delete с тем же указателем, в комплекте с его исходным типом, даже если T не имеет виртуального деструктора или имеет значение void

Ответ №2:

shared_ptr<T> особенность в том, что по дизайну разрешено хранить указатель на любой тип указателя, который преобразуется в T* и будет использовать соответствующий параметр удаления без UB! Это связано со shared_ptr<Base> p(new Derived); сценариями, но также включает shared_ptr<void> .

Например:

 #include <boost/shared_ptr.hpp>

struct T {
    T() { std::cout << "T()n"; }
    ~T() { std::cout << "~T()n"; }
};


int main() {
    boost::shared_ptr<void> sp(new T);
}
 

выдает вывод:

 $ ./test
T()
~T()
 

Если вы посещаете http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/shared_ptr.htm , прокрутите вниз до раздела задания, чтобы увидеть, что именно демонстрируется. См. http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/sp_techniques.html#pvoid для получения более подробной информации.

РЕДАКТИРОВАТЬ как отметил trinithis, это UB, если тип указателя, переданный в конструктор, является void * указателем. Спасибо, что указали на это!

Комментарии:

1. Пожалуйста, обратите внимание, что это UB, если тип указателя, переданный в конструктор, является void* указателем.

2.Даже если тип переданного указателя void* равен, разве вы не могли бы избежать неопределенного поведения, используя конструктор с двумя аргументами для передачи пользовательского средства удаления, которое выполняет что-то другое, кроме вызова delete ?

3. Применим ли этот ответ только к boost::shared_ptr or std::shared_ptr также? Я не могу найти правильную ссылку.

4. @Mark Ransom: насколько я знаю std::shared_ptr , имеет ту же функцию.

5. @Rob: Конечно, но это все равно будет аргументировать специализацию std::shared_ptr<void> , в которой устранен ctor с одним аргументом. Есть get_deleter но нет set_deleter , поэтому, если вы не передадите удаление во время построения, это приведет к неопределенному поведению позже.

Ответ №3:

Если ваш указатель был создан с помощью чего-то подобного malloc , вы можете сделать shared_ptr<void, dtor> , где dtor вызовы free . Это приведет к определенному поведению.

Но опять же, возможно, вам нужно неопределенное поведение: D

Ответ №4:

Но это действительно во многих случаях. Рассмотрим следующее:

 class T
{
public:
    ~T() { std::cout << "~Tn"; }
};

int main()
{
    boost::shared_ptr<void> sp(new T);
}
 

В обстоятельствах, когда вы пытаетесь передать подлинное void * значение в shared_ptr конструктор, вы должны получить ошибку компиляции.