#cpp
Для чего нужен был вспомогательный класс auto_ptr_ref. Нужны примеры простейшей реализации
и использования.
Ответы
Ответ 1
auto_ptr_ref - пример попытки "красивой" реализации семантики перемещения (move semantics) средствами языка С++98. Стандартный std::auto_ptr, как известно, должен перемещать право владения памятью при копировании (как конструктором копирования, так и оператором присваивания) std::auto_ptrpa(new int(42)); // `pa` владеет памятью std::auto_ptr pb = pa; // теперь `pb` владеет памятью, `pa` пуст pa = pb; // теперь `pa` владеет памятью, `pb` пуст Но это создает определенные трудности при реализации всех форм копирования для std::auto_ptr. Так как копирование модифицирует свою правую часть, параметры конструктора копирования и оператора присваивания по идее должны быть неконстантными ссылками на std::auto_ptr. Но в этом случае мы не сможем делать что-то вроде std::auto_ptr foo() { ... } ... std::auto_ptr p = foo(); т.к. функция foo() возвращает временный объект, а неконстантные ссылки нельзя привязывать ко временным объектам. Что делать? Рассмотрим проблему на отвлеченном примере. Предположим, что мы захотели реализовать некий класс S, хранящий некое целочисленное значение и перемещающий это значение в получателя (в левую часть) при копировании. Правая часть при копировании получает значение -1 struct S { int i; S(int i = -1) : i(i) {} S(S &s) : i(s.i) { s.i = -1; } S &operator =(S &s) { i = s.i; s.i = -1; return *this; } }; У нас получилось то, что надо S a(42); assert(a.i == 42); S b = a; assert(b.i == 42 && a.i == -1); a = b; assert(a.i == 42 && b.i == -1); Однако мы не можем пользоваться нашим классом вот так S foo() { return S(42); } ... S a = foo(); // ERROR S b; b = foo(); // ERROR по причине того, что неконстантную ссылку (параметр конструктора и оператора присваивания) невозможно привязать ко временному объекту (к результату foo()). В такой ситуации мы можем попробовать "выкрутиться" за счет введения промежуточного класса, который мы назовем Sref, и который умеет только ссылаться на наш класс S struct S; struct Sref { S &s; }; Добавим в наш класс S оператор конверсии к Sref, порождающий Sref-ссылку на самого себя struct S { ... operator Sref() { Sref r = { *this }; return r; } ... }; А также добавим в наш класс S конвертирующий конструктор из типа Sref и аналогичный конвертирующий оператор присваивания, которые ведут себя в соответствии с требуемой нам семантикой, но через ссылку Sref struct S { ... S(Sref sr) : i(sr.s.i) { sr.s.i = -1; } S& operator =(Sref sr) { i = sr.s.i; sr.s.i = -1; return *this; } ... }; и готово - теперь инициализация и присваивание S a = foo(); S b; b = foo(); будет прекрасно компилироваться и работать именно так, как мы хотим. Они пойдут по пути S a(foo().operator Sref()); S b; b = foo().operator Sref(); Здесь также эксплуатируется то "странноватое" свойство языка, согласно которому хоть к временным объектам и нельзя привязывать неконстантные ссылки, через временные объекты однако можно вызывать их неконстантные методы. Именно так работает и именно эту роль играет класс auto_ptr_ref в реализации функциональности std::auto_ptr. Ответ 2
У auto_ptr конструктор копирования и оператор присваивания принимают ссылку не на константу, поэтому не смогли бы конструироваться из временного объекта. Код с собственным классом, демонстрирующий проблему: struct pointer { pointer() {} pointer(pointer &) {} }; pointer create() { return pointer(); } int main() { pointer ptr(create());//Ошибка компиляции } Добавим свой pointer_ref для решения проблемы: struct pointer_ref { pointer_ref(const struct pointer&) {} }; struct pointer { pointer() {} pointer(pointer &) {} pointer(pointer_ref) {} }; int main() { pointer ptr(create());//Теперь ok } Как реализация будет делать auto_ptr_ref - не важно, главное, чтобы он решал проблему.
Комментариев нет:
Отправить комментарий