Re[3]: Про перемещение (на примере кода)
От: Muxa  
Дата: 16.03.25 11:48
Оценка: -1 :)
S>>> unique_ptr<TrackedClass> _trackedClass;

S>Вот это, получается, канонический вариант. Но не красивый — добавляет в код угловатости.


Конкретно что не устраивает?
В с++ за владение объектом и его передачу как раз таки отвечает специально для этого придуманный unique_ptr.
Re[4]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 16.03.25 13:52
Оценка: :)
Здравствуйте, Muxa, Вы писали:


S>>Вот это, получается, канонический вариант. Но не красивый — добавляет в код угловатости.


M>Конкретно что не устраивает?

M>В с++ за владение объектом и его передачу как раз таки отвечает специально для этого придуманный unique_ptr.

Ну вроде и без unique_ptr все работает за счет RVO. Наш авторитет сказал выше что гарантируется стандартом, можно опираться на это. Т.е. если можно написать проще — то нужно писать проще, без доп. оберток.
=сначала спроси у GPT=
Re[5]: Про перемещение (на примере кода)
От: rg45 СССР  
Дата: 16.03.25 14:45
Оценка: +1
Здравствуйте, Shmj, Вы писали:

M>>Конкретно что не устраивает?

M>>В с++ за владение объектом и его передачу как раз таки отвечает специально для этого придуманный unique_ptr.

S>Ну вроде и без unique_ptr все работает за счет RVO. Наш авторитет сказал выше что гарантируется стандартом, можно опираться на это. Т.е. если можно написать проще — то нужно писать проще, без доп. оберток.


На самом деле, вариант с умным указателем ближе к привычным тебе ссылочным типам. С shared_ptr было бы ещё ближе. С двумя лишь отличиями: детерминированность времени жизни (это плюс) и неспособность разруливать циклические зависимости (это минус, который частично можно компенсировать использованием weak_ptr).
--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 16.03.25 19:04
Оценка:
Здравствуйте, rg45, Вы писали:

R>На самом деле, вариант с умным указателем ближе к привычным тебе ссылочным типам. С shared_ptr было бы ещё ближе. С двумя лишь отличиями: детерминированность времени жизни (это плюс) и неспособность разруливать циклические зависимости (это минус, который частично можно компенсировать использованием weak_ptr).


А это переводит нас к вопросу: https://rsdn.org/forum/flame.comp/8890541.flat
Автор: Shmj
Дата: 30.01.25


Т.е. оно в принципе можно и std::option<std::reference_wrapper<Obj1>> писать и получить фактически те же удобные ссылки без доп. нагрузки на управление памятью. Или умные указатели — так же фактически все делает за тебя.

Но! Добавляется невыразительность и угловатость кода на пустом месте.
=сначала спроси у GPT=
Re[7]: Про перемещение (на примере кода)
От: rg45 СССР  
Дата: 16.03.25 19:41
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А это переводит нас к вопросу: https://rsdn.org/forum/flame.comp/8890541.flat
Автор: Shmj
Дата: 30.01.25


S>Т.е. оно в принципе можно и std::option<std::reference_wrapper<Obj1>> писать и получить фактически те же удобные ссылки без доп. нагрузки на управление памятью. Или умные указатели — так же фактически все делает за тебя.


S>Но! Добавляется невыразительность и угловатость кода на пустом месте.


А, вот ты о чём. Тоже мне, проблема. Алиасы же есть:

using TrackedClassPtr = std::shared_ptr<TrackedClass>;

class Wrapper {
private:
    TrackedClassPtr _trackedClass;
    
public:
// . . .    
    TrackedClassPtr get() const { return _trackedClass; }
};

TrackedClassPtr fun1() {
    Wrapper w = Wrapper();
    return w.get();
}
--
Справедливость выше закона. А человечность выше справедливости.
Re[7]: Про перемещение (на примере кода)
От: rg45 СССР  
Дата: 16.03.25 19:45
Оценка:
Здравствуйте, Shmj, Вы писали:


S>Т.е. оно в принципе можно и std::option<std::reference_wrapper<Obj1>> писать и получить фактически те же удобные ссылки без доп. нагрузки на управление памятью


Угу, а можно на потолке спать. Ты, походу, вообще предмет не просекаешь, раз пишешь эту дичь.

Если хочешь, чтоб я тебе пояснил, чего именно ты не понимаешь, перепиши вот этот свой фрагмент с использованием std::option<std::reference_wrapper<Obj1>> и я подробно тебе всё разжую. А может, дойдешь самостоятельно.

class Wrapper {
private:
    TrackedClass _trackedClass;
    
public:
    Wrapper() : _trackedClass(1) {
    }
    
    TrackedClass&& take() {
        return std::move(_trackedClass);;
    }
};

TrackedClass&& fun1() {
    Wrapper w = Wrapper();
    return w.take();
}
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 16.03.2025 20:20 rg45 . Предыдущая версия . Еще …
Отредактировано 16.03.2025 19:54 rg45 . Предыдущая версия .
Отредактировано 16.03.2025 19:49 rg45 . Предыдущая версия .
Re[8]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 16.03.25 19:49
Оценка:
Здравствуйте, rg45, Вы писали:

R>А, вот ты о чём. Тоже мне, проблема. Алиасы же есть:


Все-равно не наглядно и не удобно — держать в памяти все эти алиасы. Если бы он один был на проект то ОК. Но их же будет плодиться как мух.
=сначала спроси у GPT=
Re[9]: Про перемещение (на примере кода)
От: rg45 СССР  
Дата: 17.03.25 09:35
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Все-равно не наглядно и не удобно — держать в памяти все эти алиасы. Если бы он один был на проект то ОК. Но их же будет плодиться как мух.


Просто не нужно делать область видимости имен больше, чем это реально нужно, и всё будет норм. И это относится не только к алиасам, а вообще ко всем именам.
--
Справедливость выше закона. А человечность выше справедливости.
Re: Про перемещение (на примере кода)
От: Кодт Россия  
Дата: 17.03.25 10:10
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Код:


Поправочка: это говнокод.

S>
S>class TrackedClass {
S>private:
S>    int _id;  // это поле не инициализировано ни в одном конструкторе, кроме конструктора приведения типа.
S>    bool _isDesctructed = false;

S>public:
S>    // Default constructor
S>    TrackedClass() {  // _id = мусор
S>        std::cout << "Default constructor\n";
S>    }
    
S>    TrackedClass(int id) : _id(id) {
S>        std::cout << "Constructor id=" << id << "\n";
S>    }
    
S>    // Destructor
S>    ~TrackedClass() {
S>        _isDesctructed = true;  // это бессмысленно, после выхода из деструктора там будет мусор
S>        std::cout << "Destructor id=" << _id << "\n";
S>    }

S>    // Copy constructor
S>    TrackedClass(const TrackedClass& other) {  // _id = мусор
S>        std::cout << "Copy constructor\n";
S>    }

S>    // Move constructor
S>    TrackedClass(TrackedClass&& other) noexcept {  // _id = мусор
S>        std::cout << "Move constructor\n";
S>    }

S>    // Copy assignment operator
S>    TrackedClass& operator=(const TrackedClass& other) {
S>        std::cout << "Copy assignment operator\n";
S>        return *this;
S>    }

S>    // Move assignment operator
S>    TrackedClass& operator=(TrackedClass&& other) noexcept {
S>        std::cout << "Move assignment operator\n";
S>        return *this;
S>    }
    
S>    void test() {
S>        std::cout << "desctructed=" << (_isDesctructed ? "true" : "false") << std::endl;  // должно всегда писать false. На будущее: std::boolalpha в помощь
S>    }
S>};

S>class Wrapper {
S>private:
S>    TrackedClass _trackedClass;
    
S>public:
S>    Wrapper() : _trackedClass(1) {
S>    }
    
S>    TrackedClass&& take() {  // функция возвращает ссылку на _trackedClass
S>        return std::move(_trackedClass);;  // move ничего не делает, кроме непрозрачного (!) приведения к ссылке
S>    }
S>};

S>TrackedClass&& fun1() {
S>    Wrapper w = Wrapper();
S>    return w.take();  // функция возвращает ссылку на поле локального объекта.
S>}

S>int main(int argc, const char * argv[]) {
S>    TrackedClass&& t = fun1();  // поскольку функция вернула ссылку, то продления времени жизни нет. Так ещё и вернула она дохлую ссылку!
S>    t.test();  // вывели какой-то мусор.
S>    std::cout << "test" << std::endl;
S>}
S>


S>Тут можно запустить: https://www.programiz.com/online-compiler/7P7dO9D47lJtm


S>Мне нужно забрать владение над TrackedClass у Wrapper.


S>И вот в чем прикол. В онлайн-компиляторе вывод:


S>

S>Constructor id=1
S>Destructor id=1
S>desctructed=false
S>test


S>А у меня в XCode тот же самый код:


S>

S>Constructor id=1
S>Destructor id=1
S>desctructed=true
S>test


S>Т.е. если так попытаться заиметь владение — можно получить по башке, получается?


Тут нет "заиметь владение", тут есть "постараться потерять владение".

Если хочешь отлаживать такое добро, то выводи в консоль адрес объекта. Как-то вот так.
std::cout << "destructor " << this << " : " << std::boolalpha << _isDestructed << std::endl;


Конструкторы копирования/перемещения здесь ни разу не понадобились даже формально.
Ты можешь даже явно удалить их, и убедиться в этом. Фактически, ты просто разжился rvalue-ссылкой, но нигде ею не воспользовался для перемещений.

Если бы передавал по значению, то в некоторых местах мог бы словить оптимизацию copy elision. То есть, компилятору формально нужны конструкторы, но он ими не воспользуется.
Перекуём баги на фичи!
Re: Про перемещение (на примере кода)
От: Кодт Россия  
Дата: 17.03.25 10:26
Оценка:
Здравствуйте, Shmj, Вы писали:

По существу незаданного вопроса.

Не надо бояться промежуточных копирований, а тем более, промежуточных перемещений.
Если объект сам по себе не слишком большого размера, то даже если компилятор не оптимизирует размещение его промежуточных копий, то правильно написанный (а в ряде случаев — реализованный по умолчанию) конструктор перемещения не отнимет много процессорного времени.
Всякие там std::vector прекрасно перемещаются сами — передавая своё содержимое от одного экземпляра другому.

Объекты надо возвращать (и желательно, принимать тоже) по значению, а не по rvalue-ссылке.
Так ты с большей гарантией не получишь висячую ссылку, как в коде примера.

Соответственно, дизайн кода у обёртки — например, такой
TrackedClass Wrapper::take() { return std::move(_trackedClass); }

TrackedClass fun1() {
  Wrapper w;
  return w.take();
}

Здесь мы говорим, что ссылка на поле — временная, поэтому конструктор возвращаемого значения — это конструктор перемещения. То есть, можно украсть контент.

Если же объект реально тяжеловесный сам по себе, и не хочется морочить голову вопросами его перемещения — то умные указатели тебе в помощь. Заодно и работу со стеком разгрузишь.
std::unique_ptr не имеет конструктора копирования, так что заодно получишь диагностику от компилятора обо всех местах, где ты явно или неявно пытался копировать вместо перемещения. Ну и там, где нужно именно копирование, — сделаешь его руками.
Перекуём баги на фичи!
Re[2]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 17.03.25 10:40
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Если же объект реально тяжеловесный сам по себе, и не хочется морочить голову вопросами его перемещения — то умные указатели тебе в помощь. Заодно и работу со стеком разгрузишь.


Так std:vector же хранит свои большие данные (сам массив) — не в стеке а в куче, даже если сам vector создан в стеке. И если RVO работает — то зачем лишние обертки?
=сначала спроси у GPT=
Re[5]: Про перемещение (на примере кода)
От: Muxa  
Дата: 17.03.25 10:56
Оценка: -1 :)
S>Т.е. если можно написать проще — то нужно писать проще, без доп. оберток.

Если основной критерий выбора языка это простота, то как ты вообще оказался на этом форуме?
Я думал тебе нужен перформанс и максимальная кроссплатформенность.
Re[3]: Про перемещение (на примере кода)
От: __kot2  
Дата: 17.03.25 12:01
Оценка:
Здравствуйте, Shmj, Вы писали:

S>Здравствуйте, Кодт, Вы писали:


К>>Если же объект реально тяжеловесный сам по себе, и не хочется морочить голову вопросами его перемещения — то умные указатели тебе в помощь. Заодно и работу со стеком разгрузишь.


S>Так std:vector же хранит свои большие данные (сам массив) — не в стеке а в куче, даже если сам vector создан в стеке. И если RVO работает — то зачем лишние обертки?

У тебя вообще в плане дизайна весь код какой-то кривой. Всякие там getdata это как правило хреновый дизайн. Всегда можно без этого сделать компактнее и яснее
Re[3]: Про перемещение (на примере кода)
От: Кодт Россия  
Дата: 17.03.25 12:04
Оценка:
Здравствуйте, Shmj, Вы писали:

К>>Если же объект реально тяжеловесный сам по себе, и не хочется морочить голову вопросами его перемещения — то умные указатели тебе в помощь. Заодно и работу со стеком разгрузишь.

S>Так std:vector же хранит свои большие данные (сам массив) — не в стеке а в куче, даже если сам vector создан в стеке. И если RVO работает — то зачем лишние обертки?

Так вот вектор не тяжеловесный сам по себе. Что там внутри, один указатель и два размера.
Поэтому там конструктор перемещения сводится к трём операциям обмена.

Но если у тебя какая-нибудь мега-структура с множеством полей, которые нужно как-то копировать-переносить, — то моли бога, чтобы её можно было оптимизировать до memcpy.
А уж если эти поля с нетривиальными конструкторами...

По сути, использовать unique_ptr (или shared_ptr, возможно, в тандеме с CopyOnWrite) — это такие разновидности идиомы PIMPL. Лёгкий фасад и тяжёлая реализация (в том числе, реализация копирования).
Перекуём баги на фичи!
Re[4]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 17.03.25 14:34
Оценка:
Здравствуйте, __kot2, Вы писали:

S>>Так std:vector же хранит свои большие данные (сам массив) — не в стеке а в куче, даже если сам vector создан в стеке. И если RVO работает — то зачем лишние обертки?

__>У тебя вообще в плане дизайна весь код какой-то кривой. Всякие там getdata это как правило хреновый дизайн. Всегда можно без этого сделать компактнее и яснее

На пример как?
=сначала спроси у GPT=
Re[4]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 17.03.25 14:35
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Так вот вектор не тяжеловесный сам по себе. Что там внутри, один указатель и два размера.

К>Поэтому там конструктор перемещения сводится к трём операциям обмена.

Но это перемещения. А ведь копирование может быть весьма затратным, там же и гигабаты могут быть.
=сначала спроси у GPT=
Re[5]: Про перемещение (на примере кода)
От: __kot2  
Дата: 17.03.25 19:41
Оценка:
Здравствуйте, Shmj, Вы писали:

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


S>>>Так std:vector же хранит свои большие данные (сам массив) — не в стеке а в куче, даже если сам vector создан в стеке. И если RVO работает — то зачем лишние обертки?

__>>У тебя вообще в плане дизайна весь код какой-то кривой. Всякие там getdata это как правило хреновый дизайн. Всегда можно без этого сделать компактнее и яснее

S>На пример как?

Для этого нужно целиком задачу понимать. Сформулируй ее как нить более цельно
Re[6]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 17.03.25 22:21
Оценка:
Здравствуйте, __kot2, Вы писали:

S>>На пример как?

__>Для этого нужно целиком задачу понимать. Сформулируй ее как нить более цельно

Сетевой бинарный пакет. С помощью методов устанавливаем удобным образом отдельные части пакета — как то версия, тип пакета и пр. Но так же должна быть возможность получить данные пакета целиком для отправки (или предварительного шифрования).
=сначала спроси у GPT=
Re[7]: Про перемещение (на примере кода)
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.03.25 22:46
Оценка: -1 :)
Здравствуйте, Shmj, Вы писали:


S>Сетевой бинарный пакет. С помощью методов устанавливаем удобным образом отдельные части пакета — как то версия, тип пакета и пр. Но так же должна быть возможность получить данные пакета целиком для отправки (или предварительного шифрования).


У меня знакомый делал подобную задачу. Задаётся type_id константой, задаётся низлежащий тип, так же может быть вектор подобных значений. Он её делал как тестовое. У него всё вычислялось в компайл-тайме, и вообще, код бы на порядок, не, на два, лучше. За день вроде сделал.
Маньяк Робокряк колесит по городу
Re[8]: Про перемещение (на примере кода)
От: Shmj Ниоткуда  
Дата: 17.03.25 23:22
Оценка:
Здравствуйте, Marty, Вы писали:

M>У меня знакомый делал подобную задачу. Задаётся type_id константой, задаётся низлежащий тип, так же может быть вектор подобных значений. Он её делал как тестовое. У него всё вычислялось в компайл-тайме, и вообще, код бы на порядок, не, на два, лучше. За день вроде сделал.


В компил-тайме не получится, т.к. тип пакета узнаю на основе поступающих данных.
=сначала спроси у GPT=
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.