Вопрос по стандартному unique_ptr
От: Shmj Ниоткуда  
Дата: 08.07.23 11:52
Оценка:
Такой вопрос.

Вот такая у меня открылась декларация класса unique_ptr в VS:

_EXPORT_STD template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr { // non-copyable pointer to an object
public:
    using pointer      = typename _Get_deleter_pointer_type<_Ty, remove_reference_t<_Dx>>::type;
    using element_type = _Ty;
    using deleter_type = _Dx;


Видно что 2 параметра в шаблоне — _Ty и _Dx. Тут коммент — типа _Dx имеет значение по умолчанию = default_delete<_Ty>. Но где это значение установлено — почему его можно не указывать явно, если инструкция = default_delete<_Ty> — закомментирована?
Re: Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 08.07.23 11:54
Оценка: 8 (1) +3
Здравствуйте, Shmj, Вы писали:

S>Видно что 2 параметра в шаблоне — _Ty и _Dx. Тут коммент — типа _Dx имеет значение по умолчанию = default_delete<_Ty>. Но где это значение установлено — почему его можно не указывать явно, если инструкция = default_delete<_Ty> — закомментирована?


Очевидно, в предварительном объявлении.

Поищи выше по тексту, найдешь вот такое объявление:

_EXPORT_STD template <class _Ty, class _Dx = default_delete<_Ty>>
class unique_ptr;
--
Отредактировано 08.07.2023 12:01 rg45 . Предыдущая версия .
Re: Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 08.07.23 12:48
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Вот такая у меня открылась декларация класса unique_ptr в VS:


S>
S>_EXPORT_STD template <class _Ty, class _Dx /* = default_delete<_Ty> */>
S>class unique_ptr { // non-copyable pointer to an object
S>public:
S>    using pointer      = typename _Get_deleter_pointer_type<_Ty, remove_reference_t<_Dx>>::type;
S>    using element_type = _Ty;
S>    using deleter_type = _Dx;
S>


Декларация у него открылась

В том-то и дело, что нифига это не декларация, а дефиниция. А декларация была выше по тексту.

Я тебе когда еще говорил
Автор: rg45
Дата: 05.01.23
, чтоб ты почитал про declarations и definitions.
--
Re[2]: Вопрос по стандартному unique_ptr
От: Shmj Ниоткуда  
Дата: 09.07.23 02:45
Оценка: :))
Здравствуйте, rg45, Вы писали:

R>Очевидно, в предварительном объявлении.


Благодарю. Чего-то протупил — поиском не нашел.

И можно еще вопрос. Вот в этом же классе есть:

template <class _Ty, class _Dx>
class unique_ptr
{
// ...
private:
    template <class, class>
    friend class unique_ptr;
};


Зачем во friend добавляет сам себя? Вот что думает GPT- что это ошибка или недоразумение:

Класс по умолчанию имеет доступ к своим приватным членам без необходимости объявления friend class для себя. В данном случае, добавление friend class unique_ptr внутри класса unique_ptr является излишним и необязательным.

Вероятно, это было сделано по ошибке или по недоразумению. Добавление friend class unique_ptr внутри класса unique_ptr не имеет реальной практической пользы и может быть удалено без влияния на функциональность класса.

Re[3]: Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 09.07.23 06:00
Оценка: 4 (1) +2
Здравствуйте, Shmj, Вы писали:


S>Благодарю. Чего-то протупил — поиском не нашел.


S>И можно еще вопрос. Вот в этом же классе есть:


S>
S>template <class _Ty, class _Dx>
S>class unique_ptr
S>{
S>// ...
S>private:
S>    template <class, class>
S>    friend class unique_ptr;
S>};
S>


S>Зачем во friend добавляет сам себя? Вот что думает GPT- что это ошибка или недоразумение:


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

P.S. Хорошо ли они делают — это вопрос, на самом деле. Лично я к таким объявлениям отношусь с настороженностью. Ведь с таким объявлением приватный доступ взламывается "на раз" путем добавления какой-нибудь специализации-пустышки. Когда у меня возникает соблазн написать подобное всеобъемлющее объявление дружбы, я превым делом проверяю, все ли у меня в порядке с дизайном.
--
Отредактировано 09.07.2023 6:07 rg45 . Предыдущая версия .
Re[3]: Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 09.07.23 08:14
Оценка: :))) :)
Здравствуйте, Shmj, Вы писали:

S>Зачем во friend добавляет сам себя? Вот что думает GPT- что это ошибка или недоразумение:


Ты только не учи ее пока ни чему, ладно?
--
другой Вопрос по стандартному unique_ptr
От: Sm0ke Россия ksi
Дата: 18.07.23 11:22
Оценка:
У меня другой вопрос по unique_ptr

Если взять в него завернуть некий some_type
А сам умный указатель положить в map
То при v_map.try_emplace(v_key, std::make_unique<some_type>() ); Будет создан экземляр, даже если в мапе есть запись с тем-же ключом.
Т.е будет создан и разрушен лишний раз unique_ptr со своим динамическим экземпляром some_type

Вот если бы у unique_ptr был конструктор по принципу std::in_place_type, то этого оверхеда можно было избежать!

using my_ptr = std::unique_ptr<some_type>;
using my_map = std::map<bool, my_ptr>;

my_map v_map;
v_map.try_emplace(true, std::in_place_type<some_type>); // Жалко, что так нельзя!
v_map.try_emplace(true, std::make_unique<some_type>() ); // Так можно, но увы лишнее создание объектов может произойти!
Отредактировано 18.07.2023 11:22 Sm0ke . Предыдущая версия .
Re: другой Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 18.07.23 12:06
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>
S>v_map.try_emplace(true, std::in_place_type<some_type>); // Жалко, что так нельзя!
S>


Да, жалко, но, в принципе, решается через типы более высокого уровня, которые инкапсулируют внутри себя умные указатели:

class Outer
{
public:
  
    Outer(int i, double d, const std::string& s)
        : m_inner(std::make_unique<Inner>(i, d, s)) { }

private:
    struct Inner
    {
        int i{};
        double d{};
        std::string s{};
   };

   std::unique_ptr<Inner> m_inner;
};


void foo()
{
    std::map<int, Outer> m;
    
    m.emplace(
        std::piecewise_construct,
        std::forward_as_tuple(1),
        std::forward_as_tuple(42, 3.14, "Hello")
    );
}
--
Re[2]: другой Вопрос по стандартному unique_ptr
От: Sm0ke Россия ksi
Дата: 18.07.23 12:45
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Sm0ke, Вы писали:


S>>
S>>v_map.try_emplace(true, std::in_place_type<some_type>); // Жалко, что так нельзя!
S>>


R>Да, жалко, но, в принципе, решается через типы более высокого уровня, которые инкапсулируют внутри себя умные указатели:


R>
R>class Outer
R>{
R>public:
  
R>    Outer(int i, double d, const std::string& s)
R>        : m_inner(std::make_unique<Inner>(i, d, s)) { }

R>private:
R>    struct Inner
R>    {
R>        int i{};
R>        double d{};
R>        std::string s{};
R>   };

R>   std::unique_ptr<Inner> m_inner;
R>};


R>void foo()
R>{
R>    std::map<int, Outer> m;
    
R>    m.emplace(
R>        std::piecewise_construct,
R>        std::forward_as_tuple(1),
R>        std::forward_as_tuple(42, 3.14, "Hello")
R>    );
R>}
R>


Понятно. Так делать неудобно.

p.s: Кстати, зачем тут вы использовали piecewise_construct с туплами? Тут же можно и без них...

void foo()
{
    std::map<int, Outer> m;
    
    m.try_emplace(1, 42, 3.14, "Hello");
}
Отредактировано 18.07.2023 12:54 Sm0ke . Предыдущая версия .
Re[3]: другой Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 18.07.23 13:03
Оценка:
Здравствуйте, Sm0ke, Вы писали:


S>p.s: Кстати, зачем тут вы использовали piecewise_construct с туплами? Тут же можно и без них...


S>
S>void foo()
S>{
S>    std::map<int, Outer> m;
    
S>    m.try_emplace(1, 42, 3.14, "Hello");
S>}
S>


Просто в своем примере я использовал emplace, а он отличается от try_emplace по формальным параметрам. try_emplace более прост в использовании, а emplace позволяет работать с ключами произвольной сложности конструирования.
--
Отредактировано 18.07.2023 13:21 rg45 . Предыдущая версия .
Re: другой Вопрос по стандартному unique_ptr
От: vopl Россия  
Дата: 18.07.23 14:28
Оценка: 23 (4)
Здравствуйте, Sm0ke, Вы писали:

S>У меня другой вопрос по unique_ptr


S>Если взять в него завернуть некий some_type

S>А сам умный указатель положить в map
S>То при v_map.try_emplace(v_key, std::make_unique<some_type>() ); Будет создан экземляр, даже если в мапе есть запись с тем-же ключом.
S>Т.е будет создан и разрушен лишний раз unique_ptr со своим динамическим экземпляром some_type

S>Вот если бы у unique_ptr был конструктор по принципу std::in_place_type, то этого оверхеда можно было избежать!


Можно подменить аргумент для конструктора — передавать туда не сам объект-к-хранению, с тем чтобы впоследствии его двинуть/скопировать в map-node или выкинуть если ключ уже есть, а передавать некую фабрику, которую map активирует только если действительно нужно будет построить новый node. Примерно так:

#include <map>
#include <memory>
#include <iostream>

int main()
{
    std::map<int, std::unique_ptr<int>> map;

    struct Maker
    {
        operator std::unique_ptr<int>()
        {
            std::cout << "Maker activated" << std::endl;
            return std::make_unique<int>(220);
        }
    };

    map.try_emplace(1, Maker{});
    map.try_emplace(1, Maker{});

    return 0;
}
Re[2]: другой Вопрос по стандартному unique_ptr
От: Sm0ke Россия ksi
Дата: 19.07.23 12:01
Оценка:
Здравствуйте, vopl, Вы писали:

V>Можно подменить аргумент для конструктора — передавать туда не сам объект-к-хранению, с тем чтобы впоследствии его двинуть/скопировать в map-node или выкинуть если ключ уже есть, а передавать некую фабрику, которую map активирует только если действительно нужно будет построить новый node. Примерно так:


V>
V>#include <map>
V>#include <memory>
V>#include <iostream>

V>int main()
V>{
V>    std::map<int, std::unique_ptr<int>> map;

V>    struct Maker
V>    {
V>        operator std::unique_ptr<int>()
V>        {
V>            std::cout << "Maker activated" << std::endl;
V>            return std::make_unique<int>(220);
V>        }
V>    };

V>    map.try_emplace(1, Maker{});
V>    map.try_emplace(1, Maker{});

V>    return 0;
V>}
V>


В мейкер придётся ещё передавать параметры создания объекта, и в нём хранить. Но в целом идея ясна, спасибо
upd: Хотя выглядит как костыль)
Отредактировано 19.07.2023 12:02 Sm0ke . Предыдущая версия .
Re[3]: другой Вопрос по стандартному unique_ptr
От: rg45 СССР  
Дата: 19.07.23 13:08
Оценка: +1
Здравствуйте, Sm0ke, Вы писали:

S>В мейкер придётся ещё передавать параметры создания объекта, и в нём хранить. Но в целом идея ясна, спасибо

S>upd: Хотя выглядит как костыль)

Можно заморочиться, и сделать универсальный Maker:

http://coliru.stacked-crooked.com/a/401ae1d975a23db1

template <typename T, typename...A>
class Maker
{
public:

    explicit Maker(A&&...args) : m_args(std::forward<A>(args)...) {}

    operator std::unique_ptr<T>() &&
    {
        return std::apply(std::make_unique<T, A&&...>, std::move(m_args));
    }

private:

    std::tuple<A&&...> m_args;
};

template <typename T, typename... A>
Maker<T, A...> maker(A&&...args) { return Maker<T, A...>{ std::forward<A>(args)...}; }
--
Отредактировано 19.07.2023 13:35 rg45 . Предыдущая версия . Еще …
Отредактировано 19.07.2023 13:12 rg45 . Предыдущая версия .
Re[3]: другой Вопрос по стандартному unique_ptr
От: vopl Россия  
Дата: 19.07.23 13:36
Оценка: 1 (1)
Здравствуйте, Sm0ke, Вы писали:

S>В мейкер придётся ещё передавать параметры создания объекта, и в нём хранить. Но в целом идея ясна, спасибо


Можно передавать, а можно и не передавать и не хранить, а просто активацию фабрики вынести на уровень выше и пусть прикладник сам реализует создание объекта-к-хранению

#include <map>
#include <memory>

// библиотечный слой
template <class F>
struct Factory
{
    F f;
    operator auto()
    {
        return f();
    }
};

// прикладной
int main()
{
    std::map<int, std::unique_ptr<int>> map;

    map.try_emplace(1, Factory{[](){ return std::make_unique<int>(220);}});
    map.try_emplace(1, Factory{[](){ return std::make_unique<int>(380);}});

    return 0;
}


S>upd: Хотя выглядит как костыль)

Ну, хз, это все субъективно. Вынести переиспользуемый код в библиотеку и пользовать оттуда — как по мне, с этим вполне можно комфортно жить.
Re[4]: Вот такой еще вариант
От: rg45 СССР  
Дата: 19.07.23 14:01
Оценка: 3 (1) +1
R>Можно заморочиться, и сделать универсальный Maker:
R>http://coliru.stacked-crooked.com/a/401ae1d975a23db1

Вот такой еще вариант. Это, по-моему, даже лучше — можно легко адаптировать как под создание различных поинтеров, так и под непосредственное создание объектов:

http://coliru.stacked-crooked.com/a/3fcdc4eeaf7a5fa1

template <typename F>
class Maker
{
public:

    explicit Maker(F&& f) : m_f(std::forward<F>(f)) {}

    operator auto() && { return m_f(); }

private:

    F m_f;
};

template <typename F>
Maker(F&&) -> Maker<F>;

template <typename T, typename... A>
auto unique_maker(A&&...args) { return Maker([&]{ return std::make_unique<T>(std::forward<A>(args)...); }); }
--
Re[2]: другой Вопрос по стандартному unique_ptr
От: qaz77  
Дата: 20.07.23 09:33
Оценка:
Здравствуйте, vopl, Вы писали:

V>Можно подменить аргумент для конструктора — передавать туда не сам объект-к-хранению, с тем чтобы впоследствии его двинуть/скопировать в map-node или выкинуть если ключ уже есть, а передавать некую фабрику, которую map активирует только если действительно нужно будет построить новый node.


Только еще надо будет озаботиться, чтобы при исключении в конструкторе или bad_alloc нода как-то удалялась.
Re[3]: другой Вопрос по стандартному unique_ptr
От: vopl Россия  
Дата: 20.07.23 09:42
Оценка: 2 (1)
Здравствуйте, qaz77, Вы писали:

Q>Здравствуйте, vopl, Вы писали:


V>>Можно подменить аргумент для конструктора — передавать туда не сам объект-к-хранению, с тем чтобы впоследствии его двинуть/скопировать в map-node или выкинуть если ключ уже есть, а передавать некую фабрику, которую map активирует только если действительно нужно будет построить новый node.


Q>Только еще надо будет озаботиться, чтобы при исключении в конструкторе или bad_alloc нода как-то удалялась.


Все уже украдено до нас https://timsong-cpp.github.io/cppwp/associative.reqmts.except#2
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.