Почему оператор std :: auto_ptr = мусор?

Похоже, возникла проблема с std :: auto_ptr и присваиванием, из-за чего объект, на который есть ссылка, по какой-то причине оказывается удаленным.

std::auto_ptr<AClass> someVar = new AClass();  // should work, but mangles content
std::auto_ptr<AClass> someVar( new AClass() ); // works fine.
std::auto_ptr<AClass> someVar = std::auto_ptr<AClass>(new AClass()); // works fine.

std::auto_ptr<AClass> someVar;
someVar.reset( new AClass() ); // works fine.

Я проследил это, и выяснилось (наблюдая за значениями в отладчике), что проблема возникает при передаче указателя из временного std :: auto_ptr_byref (), созданного для обертывания указателя rhs. Это значение, содержащееся в _Right при входе в функцию auto_ptr (auto_ptr_ref ‹_Ty> _Right), является правильным, но значение в _Myptr при выходе является нежелательным.

template<class _Ty>
    struct auto_ptr_ref
        {   // proxy reference for auto_ptr copying
    auto_ptr_ref(void *_Right)
        : _Ref(_Right)
        {   // construct from generic pointer to auto_ptr ptr
        }

    void *_Ref; // generic pointer to auto_ptr ptr
    };

template<class _Ty>
class auto_ptr
    {   // wrap an object pointer to ensure destruction
public:
typedef _Ty element_type;

explicit auto_ptr(_Ty *_Ptr = 0) _THROW0()
    : _Myptr(_Ptr)
    {   // construct from object pointer
    }

auto_ptr(auto_ptr<_Ty>& _Right) _THROW0()
    : _Myptr(_Right.release())
    {   // construct by assuming pointer from _Right auto_ptr
    }

auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()
    {   // construct by assuming pointer from _Right auto_ptr_ref
    _Ty **_Pptr = (_Ty **)_Right._Ref;
    _Ty *_Ptr = *_Pptr;
    *_Pptr = 0; // release old
    _Myptr = _Ptr;  // reset this
    }
auto_ptr<_Ty>& operator=(auto_ptr_ref<_Ty> _Right) _THROW0()
{   // assign compatible _Right._Ref (assume pointer)
_Ty **_Pptr = (_Ty **)_Right._Ref;
_Ty *_Ptr = *_Pptr;
*_Pptr = 0; // release old
reset(_Ptr);    // set new
return (*this);
}

Сначала я подумал, что это испортило наследование и отсекло интерфейсы, однако это происходит, даже если у класса есть только один родительский класс.

Мы можем избежать использования = new, если вспомним, либо используя скобки, либо изменив явную временную температуру std :: auto_ptr в правой части, это, конечно, подвержено ошибкам.

Это просто сломанная версия библиотеки или какая-то основная вещь, которую я просто не понимаю?

Мы также заметили аналогичную проблему с назначением std :: auto_ptr для boot :: shared_ptr, хотя мы полностью удалили это сейчас, и я не помню, какой синтаксис вызвал проблему.


person Greg Domjan    schedule 09.01.2009    source источник
comment
Какой компилятор и версию вы используете?   -  person Michael Burr    schedule 09.01.2009
comment
См. stackoverflow.com/questions/372665/ для обсуждения. разницы между вашей первой и второй строками.   -  person Kristopher Johnson    schedule 09.01.2009
comment
Это было интересно, но напрямую не помогло решить проблему.   -  person Greg Domjan    schedule 09.01.2009
comment
Я не знаю вашей ситуации, но, если возможно, я бы предложил провести рефакторинг, чтобы вместо этого использовать интеллектуальные указатели boost / TR1. auto_ptr сложно использовать и обдумывать, и его нельзя, например, хранить в контейнерах STL.   -  person Niklas    schedule 09.01.2009


Ответы (3)


Первая строка:

std::auto_ptr<AClass> someVar = new AClass();  // should work, but mangles content

должен привести к ошибке компилятора. Поскольку неявное преобразование необработанного указателя AClass в auto_ptr (конструктор для auto_ptr, который принимает необработанный указатель, помечен как explicit), инициализация с использованием формы «инициализатор копирования» не разрешена.

VC9 выдает следующую ошибку:

C:\temp\test.cpp(23) : error C2440: 'initializing' : cannot convert from 'AClass *' to 'std::auto_ptr<_Ty>'

Другие компиляторы, которые я пробовал (GCC 3.4.5, Comeau C / C ++ 4.3.10.1, Digital Mars), дают аналогичную ошибку.

РЕДАКТИРОВАТЬ:

Похоже, что это на самом деле ошибка в реализации auto_ptr<> в VS2005 (не уверен, была ли она введена в SP1 или была в VS2005 с самого начала), которая была исправлена ​​в VS2008. Вот запись об ошибке MS Connect для проблемы:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101842&wa=wsignin1.0

person Michael Burr    schedule 09.01.2009
comment
Первая строка не имеет ничего общего с operator = (); это инициализация, а не назначение. есть ctor, который принимает auto_ptr, и ctor, который принимает необработанный указатель. ctor необработанного указателя является явным, поэтому преобразование исходного указателя в auto_ptr отсутствует. - person wilhelmtell; 09.01.2009
comment
@wilhelmtell - вы правы. Я исправил это искажение примерно в то же время, когда вы на него указали. - person Michael Burr; 09.01.2009
comment
Хотя одна из оптимизаций заключается в вызове ctor и инициализации, этого не происходит. В MSVC 2005 auto_ptr_ref был введен в библиотеку и вместо этого сконструирован как временный rhs. В предыдущих версиях я не мог выполнить строку 1, и да, я получал эту ошибку. - person Greg Domjan; 09.01.2009
comment
Как теперь показано в копии из файла ‹memory›, предоставленного в библиотеке MSVC8, вы правы, что raw ptr является явным, однако auto_ptr_ref не является явным, как и конструктор auto_ptr_ref. :( Изменение в библиотеке вызывает наибольшую боль! - person Greg Domjan; 09.01.2009
comment
Ваша ссылка немного искажена. (У меня недостаточно репутации, чтобы это исправить.) - person zdan; 10.01.2009

Известная ошибка в VC ++ между VC6 и VC9: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101842.

person MSalters    schedule 09.01.2009

Отредактировано: Майкл совершенно прав, это ошибка компиляции, так какой компилятор вы используете? Вам нужно вызвать reset, чтобы поместить новое значение в auto_ptr.

person Jesse Pepper    schedule 09.01.2009