Re[5]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 11:46
Оценка: 3 (1) +1
Здравствуйте, Skorodum, Вы писали:

S>А можете привести пример кода, который вы могли бы у себя улучшить при поддержке mandatory copy/move elision? Подозреваю, что ручная оптимизация возвращаемой из функций памяти будет не нужна.


Mandatory copy/move elision здорово облегчают написание и улучшают качество разного рода создателей "сложных" объектов — парсеров, загрузчиков, фабрик и пр. Давай условно будем называвать такую процедуру "загрузчик". "Сложные" объекты, это, в первую очередь, такие объекты, которые имеют сложные связи с внешним миром и эти связи хочется заложить прямо на этапе конструирования объекта, чтобы исключить даже саму возможность существования невалидных объектов, даже временно. Также для таких объектов очень часто хочется запретить операции копирования/перемещения, а также конструктор по умолчанию. Также к сложным объектам можно отнести большие объекты с дорогой и сложной операцией копирования. Благодаря возможности mandatory copy/move elision загрузчик имеет возможность подготовить все необходимые данные, возможно, предварительно что-то подгрузить из DB, поискать все необходимые связи в графе объектов и т.п. и, наконец, создать сам объект и вернуть его по значению — даже при запрещенных конструкторах копирования и перемещения. Что при этом очень важно — загрузчик не навязывает внешнему миру способ владения объеком — верхняя логика сама решает, будет ли она владеть объектом через какой-то смарт-поинтер и через какой именно, либо просто создаст автоматический объект на стеке (в юнит-тесте, например).
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 13.01.2023 12:18 rg45 . Предыдущая версия . Еще …
Отредактировано 13.01.2023 12:14 rg45 . Предыдущая версия .
Отредактировано 13.01.2023 12:13 rg45 . Предыдущая версия .
Re[6]: Какая версия С++ ваша основная?
От: Skorodum Россия  
Дата: 13.01.23 12:39
Оценка:
Здравствуйте, rg45, Вы писали:

R>Mandatory copy/move elision здорово облегчают написание и улучшают качество разного рода создателей "сложных" объектов — парсеров, загрузчиков, фабрик и пр.


Правильно понимаю что:
class ComplexObject {
    ComplexObject(int) {}
    friend ComplexObject createComplexObject();
    
    ComplexObject() = delete;
    ComplexObject(ComplexObject&&) = delete;
    ComplexObject(const ComplexObject&) = delete;
};

ComplexObject createComplexObject()
{
    return ComplexObject(0);
}


..единственный способ создать объект ComplexObject это вызов createComplexObject и никак иначе? И при этом никакого копирования памяти?
Re[7]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 12:51
Оценка: 3 (1) +1
Здравствуйте, Skorodum, Вы писали:

S>Правильно понимаю что:

S>
S>class ComplexObject {
S>    ComplexObject(int) {}
S>    friend ComplexObject createComplexObject();
    
S>    ComplexObject() = delete;
S>    ComplexObject(ComplexObject&&) = delete;
S>    ComplexObject(const ComplexObject&) = delete;
S>};

S>ComplexObject createComplexObject()
S>{
S>    return ComplexObject(0);
S>}
S>


S>..единственный способ создать объект ComplexObject это вызов createComplexObject и никак иначе? И при этом никакого копирования памяти?


Да, все верно.

Единственное, что хочется заметить, что совсем не обязательно закрывать прямо все конструкторы в классе. Вполне возможен вариант, что какой-то конструкор остается открытым и доступным, просто его использование может быть сопряжено с какими-то более-менее сложными сопуствующими операциями — вычитка дополнительных данных из базы, поиск связей с другими объектами и пр. Поэтому функция-загрузчик может предоставляться скорее в качестве помощника, берущего на себя все сопутствующие заботы, а не в качестве полицая, запрещающего прямое создание.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 13:06
Оценка: +1
Здравствуйте, rg45, Вы писали:

S>>Правильно понимаю что:

S>>
S>>class ComplexObject {
S>>    ComplexObject(int) {}
S>>    friend ComplexObject createComplexObject();
    
S>>    ComplexObject() = delete;
S>>    ComplexObject(ComplexObject&&) = delete;
S>>    ComplexObject(const ComplexObject&) = delete;
S>>};

S>>ComplexObject createComplexObject()
S>>{
S>>    return ComplexObject(0);
S>>}
S>>


S>>..единственный способ создать объект ComplexObject это вызов createComplexObject и никак иначе? И при этом никакого копирования памяти?


R>Да, все верно.


R>Единственное, что хочется заметить, что совсем не обязательно закрывать прямо все конструкторы в классе. Вполне возможен вариант, что какой-то конструкор остается открытым и доступным, просто его использование может быть сопряжено с какими-то более-менее сложными сопуствующими операциями — вычитка дополнительных данных из базы, поиск связей с другими объектами и пр. Поэтому функция-загрузчик может предоставляться скорее в качестве помощника, берущего на себя все сопутствующие заботы, а не в качестве полицая, запрещающего прямое создание.


Чтоб приблизить пример к более практическому применению, можно вспомнить про сериализацию:

class MyDeserializer
{
public:

  ComplexObject DeserializeComplexObject();
}


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

Ну и понятное дело, все эти фабрики, загрузчики, десериализаторы могут быть шаблонными в той или иной мере, мы здесь об этом не вспоминаем просто, чтоб сконцентрироваться на главном вопросе.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 13.01.2023 13:12 rg45 . Предыдущая версия . Еще …
Отредактировано 13.01.2023 13:10 rg45 . Предыдущая версия .
Отредактировано 13.01.2023 13:06 rg45 . Предыдущая версия .
Re[8]: Какая версия С++ ваша основная?
От: Skorodum Россия  
Дата: 13.01.23 13:31
Оценка:
Здравствуйте, rg45, Вы писали:

Спасибо. А как происходит обработка ошибок в фабрике? Там же доступны только исключения и тогда чем это отличается от того же самого прямо в конструкторе класса?
S>ComplexObject createComplexObject()
{
    // как сообщаем об ошибках здесь? Только исключения
    ...
    return ComplexObject(0);
}
Re[9]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 14:03
Оценка:
Здравствуйте, Skorodum, Вы писали:

S>Спасибо. А как происходит обработка ошибок в фабрике? Там же доступны только исключения и тогда чем это отличается от того же самого прямо в конструкторе класса?

S>
S>>ComplexObject createComplexObject()
S>{
S>    // как сообщаем об ошибках здесь? Только исключения
S>    ...
S>    return ComplexObject(0);
S>}
S>


Логически фабрику можно рассматривать как некое расширение конструктора. Соответственно и стратегия обработки ошибок такая же, как в конструкторах — исключения.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[10]: Какая версия С++ ваша основная?
От: Skorodum Россия  
Дата: 13.01.23 14:09
Оценка:
Здравствуйте, rg45, Вы писали:

R>Логически фабрику можно рассматривать как некое расширение конструктора. Соответственно и стратегия обработки ошибок такая же, как в конструкторах — исключения.

Наверное, еще std::optional можно использовать.
Re[11]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 14:24
Оценка: 4 (2)
Здравствуйте, Skorodum, Вы писали:

R>>Логически фабрику можно рассматривать как некое расширение конструктора. Соответственно и стратегия обработки ошибок такая же, как в конструкторах — исключения.

S>Наверное, еще std::optional можно использовать.

Не-не-не, это по факту будет равносильно возврату к старой схеме со смарт-поинтерами. Смотрите, какие возможности предоставляет фабрика с mandatory copy/move elision:

http://coliru.stacked-crooked.com/a/8e4325781b2be7cb

#include <iostream>
#include <string>
#include <vector>
#include <memory>

struct Foo
{
    const int id;

    explicit Foo(int id) : id(id) {}

    Foo() = delete;
    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;
    ////////////////////////////////////////////////////////////////////////////////////////////////
    Foo(Foo&&) = delete;            // перемещение не обязательно запрещать явно
    Foo& operator=(Foo&&) = delete; // оно автоматически запрещается, когда мы запрещаем копирование

    void test() const { std::cout << "Foo: " << id << std::endl; }
};

Foo make_foo()
{
    static int last_id {};
    return Foo(++last_id);
}

int main()
{
    auto foo = make_foo();
    const auto uptr = std::unique_ptr<Foo>(new Foo(make_foo()));
    const auto sptr = std::shared_ptr<Foo>(new Foo(make_foo()));

    foo.test();
    uptr->test();
    sptr->test();
}


То есть, мы можем выбирать стратегию владения объектом за пределами фабрики — вот, что важно. Это может быть любой тип смарпоинтера, а может быть просто объект на стеке с автоматическим временем жизни. Если же изменить тип результата make_foo на std::optional<Foo>, все эти возможности тут же пропадут.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 13.01.2023 14:44 rg45 . Предыдущая версия .
Re[12]: Какая версия С++ ваша основная?
От: Skorodum Россия  
Дата: 13.01.23 14:41
Оценка: +1
Здравствуйте, rg45, Вы писали:

R>Если же изменить тип результата make_foo на std::optional<Foo>, все эти возможности тут же пропадут.

Понял: мы же не может Foo в optional копировать. Спасибо.
Re[13]: Какая версия С++ ваша основная?
От: rg45 СССР  
Дата: 13.01.23 14:48
Оценка: 3 (1) +1
Здравствуйте, Skorodum, Вы писали:

R>>Если же изменить тип результата make_foo на std::optional<Foo>, все эти возможности тут же пропадут.

S>Понял: мы же не может Foo в optional копировать. Спасибо.

Да, именно. Если резюмировать, то mandatory copy/move elision можно рассматривать как возможность расширения конструкторов. Сам конструктор содержит в себе только те действия, которые свойственны непосредственно классу объекта. А над этим мы можем еще построить массу разнообразных фабрик/загрузчиков/парсеров и т.п., в которых будут выполняться действия по созданию объекта, специфичные для мира, в котором этот объект создается. И в итоге всегда будем иметь под рукой простую и удобную процедуру создания объекта.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 13.01.2023 14:52 rg45 . Предыдущая версия . Еще …
Отредактировано 13.01.2023 14:51 rg45 . Предыдущая версия .
Отредактировано 13.01.2023 14:49 rg45 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.