Russian Qt Forum

Программирование => С/C++ => Тема начата: __Heaven__ от Февраль 02, 2017, 10:04



Название: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 02, 2017, 10:04
Привет, друзья!
Прошу помочь разобраться, почему следующий код не верен.
Код
C++ (Qt)
#include <iostream>
#include <memory>
#include <map>
 
using namespace std;
 
class A{
public:
   A(){}
   A(const A &) = delete;
   A(A&&) = default;
   A &operator = (const A &) = delete;
   A &operator = (A&&) = default;
};
 
int main()
{
   std::map<int, unique_ptr<A>> map ={
       {1, make_unique<A>()}
   };
}
 
По идеи в правой части список инициализации с xvalue. Почему компиляторы требуют от меня реализации копирования?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Racheengel от Февраль 02, 2017, 11:53
Наверное потому, что помещение в std::map - это копирование?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 02, 2017, 13:41
Конструкторы std::map (http://www.cplusplus.com/reference/map/map/map/).

Конструктор (5):
Цитировать
(5) initializer list constructor
    Constructs a container with a copy of each of the elements in il.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 02, 2017, 14:09
Конструкторы std::map (http://www.cplusplus.com/reference/map/map/map/).

Конструктор (5):
Цитировать
(5) initializer list constructor
    Constructs a container with a copy of each of the elements in il.
Туда-то я как-то и не догадался первым делом посмотреть. Спасибо.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 02, 2017, 15:24
Ради интереса потом можете попробовать засунуть unique_ptr<A> в QMap ;).


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 02, 2017, 16:23
Не, всё та же проблема с копированием unique_ptr


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 02, 2017, 16:25
Подглядел где-то, что shared_ptr подойдёт для моих нужд.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 02, 2017, 16:29
Кстати, быть может это будет и правильнее с точки зрения проектирования. Ведь мне необходимо в некоторых методах возвращать указатель на объект, так пусть он и будет завёрнут в shared_ptr.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 02, 2017, 17:02
Ведь мне необходимо в некоторых методах возвращать указатель на объект, так пусть он и будет завёрнут в shared_ptr.

Это плохая идея - возвращать голый указатель на объект под управлением умного указателя. Нужно стараться обходиться unique_ptr, shared_ptr, weak_ptr и ссылками на объект.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 02, 2017, 17:20
Допустим желанная мапа <int, unique_ptr> создана. Как она должна работать7
Код:
auto val = theMap.value(1);  // и после этого мапа хранит пустой указатель по ключу 1
Смысл?

Кстати, быть может это будет и правильнее с точки зрения проектирования. Ведь мне необходимо в некоторых методах возвращать указатель на объект, так пусть он и будет завёрнут в shared_ptr.
Это часто хорошо, но не всегда, во всяком случае это действие не безобидно


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Авварон от Февраль 02, 2017, 23:21
Допустим желанная мапа <int, unique_ptr> создана. Как она должна работать7
Код:
auto val = theMap.value(1);  // и после этого мапа хранит пустой указатель по ключу 1
Смысл?

У std::map нет метода value.
operator[] и at() возвращают ссылку.
Вы вот так хвалитесь незнанием букваря, а лучше бы поучили стандартную библиотеку, там многое сделано очень грамотно и обоснованно для кучи разных юзкейзов (скорость, exception-safety и тп)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Авварон от Февраль 02, 2017, 23:40
Ради интереса потом можете попробовать засунуть unique_ptr<A> в QMap ;).

Забавно, что корни этой проблемы лежат не в лени кутешников, а в... фанфары... copy-on-write.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 03, 2017, 13:36
Забавно, что корни этой проблемы лежат не в лени кутешников, а в... фанфары... copy-on-write.

Точно :). Ещё стоит отметить любовь кутешников к смешиванию нескольких функциональностей/техник в одном флаконе, без возможности выбора. Implicit Sharing в контейнерах штука может и полезная, но, как выясняется, не всегда. Такие контейнеры не могут хранить уникальные объекты, что сужает область их применения.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 03, 2017, 14:30
У std::map нет метода value.
operator[] и at() возвращают ссылку.
И что, получив такую ссылку я должен все время следить не скопирована ли она?

Такие контейнеры не могут хранить уникальные объекты, что сужает область их применения.
А что (какой ф-ционал) хотелось получить? И что здесь понимается под уникальностью? Только один владеет? (unique_ptr). Тогда зачем помещать его в контейнер - ведь это подразумевает обращение к этому контейнеру.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 03, 2017, 14:42
А что (какой ф-ционал) хотелось получить? И что здесь понимается под уникальностью? Только один владеет? (unique_ptr). Тогда зачем помещать его в контейнер - ведь это подразумевает обращение к этому контейнеру.

А зачем вообще нужен unique_ptr? Функционал такой же, как при использовании unique_ptr, только с возможностью хранить несколько объектов, а не один.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Авварон от Февраль 03, 2017, 14:58
И что, получив такую ссылку я должен все время следить не скопирована ли она?


Эм, компилятор выдаст ошибку при попытке скопировать, на то он и юник.

А что (какой ф-ционал) хотелось получить? И что здесь понимается под уникальностью? Только один владеет? (unique_ptr). Тогда зачем помещать его в контейнер - ведь это подразумевает обращение к этому контейнеру.

Код:
class Node
{
public:
    Node *child(int index) { return _children.at(index).get(); }

private:
    std::vector<std::unique_ptr<Node>> _children;
};


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 03, 2017, 15:23
Код:
class Node
{
public:
    Node *child(int index) { return _children.at(index).get(); }

private:
    std::vector<std::unique_ptr<Node>> _children;
};
Смешивать "голых с умными" совсем нехорошо. Но даже если и так - какую же выгоду мы поимели с unique_ptr? Вообще часто замечал что вумные указатели лепятся без каких-либо оснований, типа "это круто/грамотно"  :)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Авварон от Февраль 03, 2017, 15:41
Смешивать "голых с умными" совсем нехорошо. Но даже если и так - какую же выгоду мы поимели с unique_ptr? Вообще часто замечал что вумные указатели лепятся без каких-либо оснований, типа "это круто/грамотно"  :)

1) Не надо писать руками qDeleteAll().
2) Exception-safety.
Код:
_children.push_back(new Node); // oooops!


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 03, 2017, 16:27
Ведь мне необходимо в некоторых методах возвращать указатель на объект, так пусть он и будет завёрнут в shared_ptr.

Это плохая идея - возвращать голый указатель на объект под управлением умного указателя. Нужно стараться обходиться unique_ptr, shared_ptr, weak_ptr и ссылками на объект.
Согласен. Я только в начале пути освоения этих шаблонов и проект местами пока что требует голые указатели :)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 03, 2017, 16:35
Код:
class Node
{
public:
    Node *child(int index) { return _children.at(index).get(); }

private:
    std::vector<std::unique_ptr<Node>> _children;
};
Смешивать "голых с умными" совсем нехорошо.
Критикуешь - предлагай. Как вернуть указатель на потомка?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Авварон от Февраль 03, 2017, 16:52
Критикуешь - предлагай. Как вернуть указатель на потомка?

Ну в данном примере можно и ссылку. Или указатель.
Вообще, то что там под "капотом" юник волновать никого не должно - либо мы, когда получаем указатель, имеем овнершип (и должны удалять), либо не имеем.
Ссылка явно говорит о том, что не имеем. Возврат шаред/юник - наоборот. Возврат голого указателя заставляет читать документацию (привет, Qt).


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 03, 2017, 16:59
Встречал рекомендацию детей хранить как weak, а их родителей как shared.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ssoft от Февраль 03, 2017, 17:22
Критикуешь - предлагай. Как вернуть указатель на потомка?

А не нужно с указателями работать, нужно с ссылками - обычные или std::reference_wrapper, а указатели глубокоо внутри держать.
Со ссылкой так вольно уже не позабавишься), придется как минимум преобразовывать.

А вообще, умные указатели unique_ptr и shared_ptr в простейшем виде реализуют понятие агрегации - обобщенной и композитной. Но с ними должны быть связаны и ассоциации, которые не реализуют агрегацию в принципе. Такого рода ассоциации могут быть реализованы разными способами.

Если повсеместно в явном виде в API используются unique_ptr и shared_ptr, то естественно тогда и голые указатели использовать (для shared реализован еще и безопасный weak). Но будьте готовы, что сам экземпляр объекта можно будет "украсть" (std::move в помощь).

Поэтому лучше всего умные указатели скрывать где-то внутри, а для доступа к данным и реализаций ассоциаций использовать всякого рода ссылки.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 03, 2017, 18:18
Если упростить, то должна быть возможность владеть объектом, и ссылаться на него (в определённый момент времени получить доступ к объекту, не владея им). unique_ptr и shared_ptr владеют объектом, а weak_ptr ссылается на него. Для unique_ptr нет "ссылающегося" умного указателя (аналога weak), остаются только голый указатель и ссылка, что совсем не умно и не безопасно. Так что в текущих std реалиях для организации владения и связей объектов безопасно можно использовать связку shared_ptr/weak_ptr.

При использовании shared_ptr голый указатель можно заменить на weak_ptr. Какой алгоритм работы с голым указателем?
Код
C++ (Qt)
void SomeClass::someMethod( SomeObject * object_ptr )
{
   if ( object_ptr != nullptr )
   {
       SomeObject & object = *object_ptr;
       object.doSomething();
   }
}
 

Причём проверка ( object_ptr != nullptr ) не даёт особых гарантий. Объект может быть удалён, а указатель будет продолжать указывать на мусор. С использованием weak_ptr алгоритм аналогичный:
Код
C++ (Qt)
void SomeClass::someMethod( weak_ptr< SomeObject > object_weak )
{
   shared_ptr< SomeObject > object_guard = object_weak.lock();
   if ( object_guard )
   {
       SomeObject & object = *object_guard;
       object.doSomething();
   }
}
 

В этом случае гарантий больше. Во-первых, можно проверить, что объект существует на момент обращения к нему. Во-вторых, время жизни объекта продлевается на время обращения к нему (на время существования object_guard). Даже если все  внешние shared_ptr< SomeObject > удалятся, метод SomeClass::someMethod сможет нормально завершить работу с переданным объектом.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 03, 2017, 19:51
Код
C++ (Qt)
void SomeClass::someMethod( weak_ptr< SomeObject > object_weak )
...
 

В этом случае гарантий больше. Во-первых, можно проверить, что объект существует на момент обращения к нему. Во-вторых, время жизни объекта продлевается на время обращения к нему (на время существования object_guard). Даже если все  внешние shared_ptr< SomeObject > удалятся, метод SomeClass::someMethod сможет нормально завершить работу с переданным объектом.
Если это просто аргумент метода/ф-ции, то нет особой разницы что предавать (хоть голый) т.к. никто не собирается ни владеть ни удалять - попользовался в методе и все. Реально проблема возникает когда "не владеет (и не создает) но хранит и пользуется". Связка shared + weak неплоха но всех проблем не решает, напр
Код
C++ (Qt)
void SomeClass::someMethod( void )
{
   shared_ptr< SomeObject > guard = m_object_weak.lock(); // член m_object_weak
   if  (guard)
   {
     ...
   }
   else {
    // И здесь что ???
   }
}
Не всегда во второй ветке можно ничего не делать. А объявлять m_object_shared (вместо m_object_weak) - свои минусы, выходит как бы "2 владельца", изменив одного надо перезарядить и второго

Update: и вообще стоит ли (упорно) добиваться чтобы абсолютно все указатели были так или иначе вумными? Мое мнение - нет, не стоит, лучше пусть небольшая часть. Но если уж вумный - не должно быть таких голых


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Old от Февраль 04, 2017, 17:48
Update: и вообще стоит ли (упорно) добиваться чтобы абсолютно все указатели были так или иначе вумными? Мое мнение - нет, не стоит, лучше пусть небольшая часть. Но если уж вумный - не должно быть таких голых
Конечно стоит. Именно все указатели должны быть умными, в 21 веке то. А если используешь умные указатели, то о голых нужно полностью забыть (или извращения в коде неизбежны).
К сожалению legacy Qt не позволяет это сделать, но к радости программирование не ограничивается только Qt.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 05, 2017, 00:32
Цитировать
Language is called c++ and not ++c because the language is improved but many people use it as C.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 05, 2017, 12:59
Код
C++ (Qt)
struct SomeClass {
 ...
 shared_ptr<SomeData> m_data;
 ...
};
Вроде бы все норм. Теперь не надо инициализировать m_data в конструкторе и удалять в деструкторе. И он может быть нулевым, поэтому умный указатель, а не просто член SomeData. Но вот неясно, действительно ли мы собирались его шарить? Увидев такое объявление - надо ли иметь ввиду что неск SomeClass юзают один m_data? Или это так просто, "умный указатель лучше"?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Old от Февраль 05, 2017, 13:23
Увидев такое объявление - надо ли иметь ввиду что неск SomeClass юзают один m_data? Или это так просто, "умный указатель лучше"?
А для такого объявления ваши вопросы уже не актуальны? :)

Код
C++ (Qt)
struct SomeClass {
 ...
 SomeData *m_data;
 ...
};

А если еще такое, то без документации (просмотра исходников) не понять, а что это за указатель и кто должен удалять данные по этому указателю.
Код
C++ (Qt)
struct SomeClass {
 ...
 SomeData *data() const;
 ...
};


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ssoft от Февраль 07, 2017, 19:10
Если упростить, то должна быть возможность владеть объектом, и ссылаться на него (в определённый момент времени получить доступ к объекту, не владея им). unique_ptr и shared_ptr владеют объектом, а weak_ptr ссылается на него. Для unique_ptr нет "ссылающегося" умного указателя (аналога weak), остаются только голый указатель и ссылка, что совсем не умно и не безопасно. Так что в текущих std реалиях для организации владения и связей объектов безопасно можно использовать связку shared_ptr/weak_ptr.

Если говорить строго, то владение, ссылание - это все отношения ассоциаций, а shared_ptr/weak_ptr, unique_ptr и любой my_ptr - все это инструменты управления временем жизни экземпляра объекта с определенными свойствами, с помощью которых уже могут быть реализованы любые ассоциативные связи.

В зависимости от конкретного случая вполне оправдано использование инструментов в любом сочетании shared/weak, shared/raw, unique/raw (raw - "голый" указатель), shared/my_any, так как все они имеют разную стоимость своего использования. Нужна потокобезопасность - считайте ссылки shared/weak, нужна скорость - используйте unique/raw и т.д., валидность указателей обеспечивайте другим способом (логикой использования).

Не обязательно shared/weak инструментарий должен реализовывать только обобщенное владение, на этой основе вполне логично реализуется и уникальное владение, и неявно обобщенное. В этом же ключе - нет никакого смысла реализовывать weak для unique, так как реализация такого unique должна повторить shared (на случай продления времени жизни экземпляру с помощью guard в примере).

Для практического применения на первый план выходит реализация конкретной ассоциативной связи, а способ ее внутренней реализации может (и должен) быть скрыт глубоко внутри. Ассоциативная связь подразумевает некоторое представление самого экземпляра объекта, в простейшем случае псевдонима (ссылки). И указатели во всем их разнообразии все же не являются представлением экземпляра, таким представлением являются разнообразные умные ссылки - представители объекта с разными свойствами.

К сожалению механизм умных ссылок вообще почти не продуман в стандарте, появилась только простейшая реализация в виде std::reference_wrapper, но сам принцип их построения понятен - использование паттерна Adapter (Wrapper). В соседней теме про корову есть наброски пары реализаций ImplicitWrapper. :)

В любом случае, соблазн использовать raw указатели всегда очень велик, особенно на начальном этапе при непродуманной архитектуре - потом выясним ху из ху.  :D Использование внятных абстракций типа wrapper, существенно повышают качество кода и понятность модели, но требуют наличия взаимодействия достаточно большого количества сущностей реализующих - ассоциации, продление времени жизни, доступ на чтение, доступ на запись и др.  ;)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 07, 2017, 20:21
Использование внятных абстракций типа wrapper, существенно повышают качество кода и понятность модели, но требуют наличия взаимодействия достаточно большого количества сущностей реализующих - ассоциации, продление времени жизни, доступ на чтение, доступ на запись и др.  ;)

Ещё хорошо продуманные wrapper не позволяют ерунду в коде писать :). По крайней мере всячески этому сопротивляются, и заставляют думать, где какое владение использовать и какие типы связей должны быть между объектами на уровне модели. И подход "скомпились уже и отстань от меня", как можно было писать в случае с голыми указателями, с wrapper уже не прокатывает :).

А текущие unique_ptr, shared_ptr, weak_ptr зачастую используются как некий частный случай сборщика мусора и не более.


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Old от Февраль 08, 2017, 00:58
В зависимости от конкретного случая вполне оправдано использование инструментов в любом сочетании

shared/raw, unique/raw
Никогда не оправдано. Только вернули raw - полностью потеряли контроль над объектом.

shared/my_any
Это вообще как?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: __Heaven__ от Февраль 08, 2017, 07:30
shared/my_any
Это вообще как?
Подозреваю, что так
Код
C++ (Qt)
my_any(shared.get());


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 08, 2017, 08:26
Ещё хорошо продуманные wrapper не позволяют ерунду в коде писать :). По крайней мере всячески этому сопротивляются, и заставляют думать, где какое владение использовать и какие типы связей должны быть между объектами на уровне модели. И подход "скомпились уже и отстань от меня", как можно было писать в случае с голыми указателями, с wrapper уже не прокатывает :).
Так-то оно так, но для объявления. А если указатель подается в ф-цию/метод, то в подавляющем большинстве случаев никакой умности не требуется. Сравним
Код
C++ (Qt)
void DoSomething( const SomeData * data );
void DoSomething( shared_ptr<SomeData> data );
 
Теперь (2-я строка) ф-ция не сможет удалить данные! И гарантируется что data будет жить даже если вызывающий удален! (актуально при multi-threading). Не очень-то веские причины/резоны. Выходит из заявления shared_ptr ничего по существу не следует, и на него можно не обращать внимания. Зачем тогда заявлять?


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 08, 2017, 12:51
Так-то оно так, но для объявления. А если указатель подается в ф-цию/метод, то в подавляющем большинстве случаев никакой умности не требуется. Сравним
Код
C++ (Qt)
void DoSomething( const SomeData * data );
void DoSomething( shared_ptr<SomeData> data );
 
Теперь (2-я строка) ф-ция не сможет удалить данные! И гарантируется что data будет жить даже если вызывающий удален! (актуально при multi-threading). Не очень-то веские причины/резоны. Выходит из заявления shared_ptr ничего по существу не следует, и на него можно не обращать внимания. Зачем тогда заявлять?

Как раз и следует:
Теперь (2-я строка) ф-ция не сможет удалить данные! И гарантируется что data будет жить даже если вызывающий удален! (актуально при multi-threading).

И это достаточно веские резоны. Если на этапе проектирования решено, что объект находится в совместном владении, то в общем случае не известно, когда и кем он будет удалён. Может вызывающий код не хочет, чтоб какая-то левая функция удалила объект без его ведома? Поэтому и передаёт другим в пользование shared_ptr. Чтобы выразить факт того, что функция DoSomething гарантировано удалит объект (или возьмёт время его жизни в своё единоличное управление), то в своей сигнатуре она должна использовать unique_ptr. И вызывающий код туда ничего кроме unique_ptr передать не сможет. Он должен передать владение объектом в функцию с помощью move-семантики (http://stackoverflow.com/questions/3106110/what-are-move-semantics).


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ssoft от Февраль 08, 2017, 15:30
shared/raw, unique/raw
Никогда не оправдано. Только вернули raw - полностью потеряли контроль над объектом.

Под контролем здесь скорее всего подразумевается управление временем жизни экземпляра объекта. То есть простой механизм сбора мусора?

Ассоциативные связи агрегации (shared/composite) обеспечивают управление временем жизни экземпляра, а вот другие виды ассоциативных связей совершенно не обязаны это делать. Как задать ассоциативную связь отличную от агрегации для unique_ptr, кроме как raw? Обычная ссылка не подойдет, так как во-первых ссылка и указатель сущности разной природы, а во-вторых ссылка не может быть изменена в дальнейшем. Для shared в критических местах тоже лишний раз счетчик трогать нежелательно. Если есть алгоритмическая гарантия того, что экземпляр не удалится во время выполнения (например, заранее заведен guard), то вполне резонно использовать и raw указатель.

Другое дело не рекомендовать использовать сочетания shared/raw, а рекомендовать shared/weak, где это возможно. A shared/raw оставить для продвинутого использования, с учетом того что имеется понимание опасности того, что это за собой влечет.

Для того чтобы совсем отказаться от raw указателей не хватает еще достаточного количества сущностей в стандарте.

shared/my_any
Это вообще как?

Это может быть реализовано как угодно). Можно и свой огород налепить, например, логировать операции с указателем, считать какую-нибудь статистику и т.п., был бы прикладной смысл. Кстати, под shaed не обязательно только shared_ptr понимать.  ;D


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Old от Февраль 08, 2017, 15:52
Под контролем здесь скорее всего подразумевается управление временем жизни экземпляра объекта. То есть простой механизм сбора мусора?
Нет. Я говорю о методах класса, которые возвращают raw-указатель. При возврате умного указателя по нему можно предположить, что с объектом может произойти и что с ним можно делать. С raw указателем это не так.

Обычная ссылка не подойдет, так как во-первых ссылка и указатель сущности разной природы, а во-вторых ссылка не может быть изменена в дальнейшем.
Что значит "сущности разной природы"? Чем это ссылки не подходят?
И что значит "ссылка не может быть изменена в дальнейшем"? Храните указатель, а возвращайте ссылку. Часто этого достаточно.

Сразу хочу отметить, я говорю про указатели отдаваемые наружу. Внутри некоего класса иногда можно использовать raw-указатели, но отдавать его наружу очень опасно (а c shared_ptr так вообще).

Для того чтобы совсем отказаться от raw указателей не хватает еще достаточного количества сущностей в стандарте.
Пишите свои. Используйте ссылки. Отказаться от raw-указателей можно было уже много лет назад. :)

Это может быть реализовано как угодно). Можно и свой огород налепить, например, логировать операции с указателем, считать какую-нибудь статистику и т.п., был бы прикладной смысл. Кстати, под shaed не обязательно только shared_ptr понимать.  ;D
Ааа. А то у меня my_any ассоциировалось с boost::any. И я испугался. :)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: Igors от Февраль 08, 2017, 16:05
Может вызывающий код не хочет, чтоб какая-то левая функция удалила объект без его ведома? Поэтому и передаёт другим в пользование shared_ptr. Чтобы выразить факт того, что функция DoSomething гарантировано удалит объект (или возьмёт время его жизни в своё единоличное управление), то в своей сигнатуре она должна использовать unique_ptr. И вызывающий код туда ничего кроме unique_ptr передать не сможет. Он должен передать владение объектом в функцию с помощью move-семантики (http://stackoverflow.com/questions/3106110/what-are-move-semantics).
Пример
Код
C++ (Qt)
void DoSomething( SomeData * data )
{
 ...
 delete data;
}
 
Это и так грубая ошибка/просмотр, оставляющая вызывающего с невалидным указателем. Если есть необходимость мочить указатель в ф-ции (достаточно редкий случай), то просто и хорошо
Код
C++ (Qt)
void DoSomething( SomeData *& data )
{
 ...
 delete data;
 data = new ...
// data = 0;
}
 

И это достаточно веские резоны. Если на этапе проектирования решено, что объект находится в совместном владении,
Хорошо, допустим решено, но сколько данных реально шарится? Да %20 отсилы, как бы не меньше
 
то в общем случае не известно, когда и кем он будет удалён.
В подавляющем большинстве случаев - прекрасно известно. Напр
Код
C++ (Qt)
// вызывающий
DoSomething(theSharedData.get());
 
Не вижу здесь ничего плохого. Написал в ф-ции delete - ну сам "злобный Буратино", в конце-концов мог и в самой ф-ции сделать get и delete. Время жизни - для ф-ции forever. Так от каких мифических угроз защищаемся? А вот утяжеление кода и, особенно, затруднение отладки - минусы очень ощутимые.

Для того чтобы совсем отказаться от raw указателей не хватает еще достаточного количества сущностей в стандарте.
Да, shared_ptr гораздо чаще лепится для "сборки мусора" (другого-то ничего нет), а не потому что действительно шарится


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ssoft от Февраль 08, 2017, 16:38
Обычная ссылка не подойдет, так как во-первых ссылка и указатель сущности разной природы, а во-вторых ссылка не может быть изменена в дальнейшем.
Что значит "сущности разной природы"? Чем это ссылки не подходят?
И что значит "ссылка не может быть изменена в дальнейшем"? Храните указатель, а возвращайте ссылку. Часто этого достаточно.

Об этом я как раз и писал раннее, что вместо указателей необходимо использовать различные виды умных ссылок (wrapper), простейшим из которых является std::reference_wrapper. Указатель в явном виде никогда не говорит о том какую конкретно ассоциацию он реализует (кроме unique возможно). Ссылка - это представитель объекта, псевдоним, для использующей стороны она выглядит как сам объект, указатель не выглядит как объект.

Обычная ссылка - это Type & или const Type &, она не может быть изменена в дальнейшем.


Сразу хочу отметить, я говорю про указатели отдаваемые наружу. Внутри некоего класса иногда можно использовать raw-указатели, но отдавать его наружу очень опасно (а c shared_ptr так вообще).

Для того чтобы совсем отказаться от raw указателей не хватает еще достаточного количества сущностей в стандарте.
Пишите свои. Используйте ссылки. Отказаться от raw-указателей можно было уже много лет назад. :)

Вот свои и пишем  ;D, и получаем очередной зоопарк решений. А при стыковке нескольких решений, да если еще и Qt приляпать, в ход идут добрые, старые и знакомые raw указатели. Так что не так-то просто от них совсем отказаться. ;)


Название: Re: std::map<..., unique_ptr> инициализация
Отправлено: ViTech от Февраль 08, 2017, 17:47
В подавляющем большинстве случаев - прекрасно известно. Напр
Код
C++ (Qt)
// вызывающий
DoSomething(theSharedData.get());
 
Не вижу здесь ничего плохого. Написал в ф-ции delete - ну сам "злобный Буратино", в конце-концов мог и в самой ф-ции сделать get и delete. Время жизни - для ф-ции forever.

Неизвестно.

В С++ имея хоть какой-нибудь доступ к объекту можно его убить. Элементарно:
Код
C++ (Qt)
void DoSomething( SomeClass & some_object )
{
   delete &some_object;
}
 

Никто и не пикнет. А кто вздумает const'ом защищаться, того const_cast'ом обезоружить ;D. Но за такое надо по голове очень сильно бить. И это другая история.

Так от каких мифических угроз защищаемся? А вот утяжеление кода и, особенно, затруднение отладки - минусы очень ощутимые.

Защищаемся от разыменования nullptr и обращения к убитым объектам. Управляем временем жизни объекта. И в сигнатуре функции, в типах входящих и возвращаемых параметрах, яснее сообщается, что можно делать с объектом, а что нельзя.

И не надо использовать методы умных указателей, которые возвращают raw-указатели. Лучше вообще считать, что их нет( get() и им подобные). Нужны они в особых случаях и для сопряжения с legacy-кодом.