Мувать не всегда дешево
#новичкам
С приходом мув семантики настали "прекрасные плюсы будущего". Нет никакого копирования, чудо-оптимизации бороздят просторы стека и кучи. Не жизнь, а сказка.
Но мир не такой уж солнечный и приветливый. Это очень опасное...
Если вы придерживаетесь RAII, пользуетесь контейнерами и умными указателями, то вы практически всегда пользуетесь правилом нуля и никогда не определяете самостоятельно специальные методы класса и, в частности, конструктор перемещения и оператор перемещающего присваивания. Компилятор сгенерирует их за вас, ленивых дядь.
Рано или поздно вы немного отрываетесь от "низов": вас уже не интересует КАК конкретно эти методы реализованы. Вы оперируете более высокоуровневыми сущностями и полагаетесь на компилятор.
И вот вы в ситуации, когда у вас есть данные, обернутые в класс, которые легально по контексту кода можно мувнуть или скопировать. Условно говоря, у вас есть функция Process, которая принимает данные по значению, чтобы поддержать оба варианта передачи: копирование и мув:
Что выбрать?
"Конечно мувнуть, это же не долгое копирование, выполнится быстро" - вот к таким не совсем корректным мыслям может привести "оторванность от низов".
Кажется, что у некоторых людей есть ощущение, что данные из одного объекта как-то перетекают в другой объект и это происходит очень быстро.
Но это не так! Перемещение - это поверхностное копирование.
Возьмем простой пример:
Что будет происходить при перемещении
Чуть сложнее:
Что будет при перемещении
Можно еще занулить конечно, но это редко происходит из соображений перфоманса.
Получается, что реально "переместить" вы можете только данные, выделенные на куче. И то они никуда не перемещаются. Вы просто копируете указатель из одного объекта в другой, при этом сами данные никак не затрагиваются.
Более того. Даже если вы используете std::string, то не всегда мув будет быстрее копирования! Thanks to SSO.
Получается, что никто никуда не течет. Все так же пресловуто копируется, кроме динамических данных под указателями.
Теперь снова актуализируем вопрос: мувать или копировать?
И ответ уже не плоскости оптимизации, а в плоскости логики кода. Перемещайте, когда вам в текущем скоупе объект больше не нужен и копируйте, если нужен. Тогда вы не пытаетесь оптимизировать код, а передаете владение объектом другому коду. Редко, когда вы на авито продаете вещи, чтобы заработать. Вы их продаете, чтобы от лишнего избавиться и дать их тем, кому они нужны, особой выгоды не ожидая. Вот здесь примерно это и должно происходить.
В реальности все немного сложнее и всегда будут исключения, но просто хочу обратить внимание, что мув семантика - это в первую очередь про передачу владения объектом и только потом уже оптимизация.
Think logically. Stay cool.
#cppcore #cpp11
#новичкам
С приходом мув семантики настали "прекрасные плюсы будущего". Нет никакого копирования, чудо-оптимизации бороздят просторы стека и кучи. Не жизнь, а сказка.
Но мир не такой уж солнечный и приветливый. Это очень опасное...
Если вы придерживаетесь RAII, пользуетесь контейнерами и умными указателями, то вы практически всегда пользуетесь правилом нуля и никогда не определяете самостоятельно специальные методы класса и, в частности, конструктор перемещения и оператор перемещающего присваивания. Компилятор сгенерирует их за вас, ленивых дядь.
Рано или поздно вы немного отрываетесь от "низов": вас уже не интересует КАК конкретно эти методы реализованы. Вы оперируете более высокоуровневыми сущностями и полагаетесь на компилятор.
И вот вы в ситуации, когда у вас есть данные, обернутые в класс, которые легально по контексту кода можно мувнуть или скопировать. Условно говоря, у вас есть функция Process, которая принимает данные по значению, чтобы поддержать оба варианта передачи: копирование и мув:
void Process(Data data);
Что выбрать?
"Конечно мувнуть, это же не долгое копирование, выполнится быстро" - вот к таким не совсем корректным мыслям может привести "оторванность от низов".
Кажется, что у некоторых людей есть ощущение, что данные из одного объекта как-то перетекают в другой объект и это происходит очень быстро.
Но это не так! Перемещение - это поверхностное копирование.
Возьмем простой пример:
struct Data {
int a;
double b;
};
Data obj1{3, 3.14};
Data obj2 = std::move(obj1);Что будет происходить при перемещении
obj1? Копирование a и b.Чуть сложнее:
struct Data {
std::array<int, 5> arr;
};
Data obj1{.arr = {1, 2, 3, 4, 5}};
Data obj2 = std::move(obj1);Что будет при перемещении
obj1, а значит и arr? Тоже копирование! std::array - это массив, фиксированного размера, расположенный на стеке. Как вы собираетесь его перемещать в другой объект? Под другой объект уже выделена своя память на стеке, вы не можете один кусок стека переместить в другой. Вы можете только скопировать значения.Можно еще занулить конечно, но это редко происходит из соображений перфоманса.
Получается, что реально "переместить" вы можете только данные, выделенные на куче. И то они никуда не перемещаются. Вы просто копируете указатель из одного объекта в другой, при этом сами данные никак не затрагиваются.
struct Data {
std::string * str;
// member functions for making it work properly
};
Data obj1{.str = new std::string("Hello, World!")};
Data obj2 = std::move(obj1);obj2 теперь имеет такое же значение указателя str, как и obj1, но сама строка оказалась нетронутой.Более того. Даже если вы используете std::string, то не всегда мув будет быстрее копирования! Thanks to SSO.
Получается, что никто никуда не течет. Все так же пресловуто копируется, кроме динамических данных под указателями.
Теперь снова актуализируем вопрос: мувать или копировать?
И ответ уже не плоскости оптимизации, а в плоскости логики кода. Перемещайте, когда вам в текущем скоупе объект больше не нужен и копируйте, если нужен. Тогда вы не пытаетесь оптимизировать код, а передаете владение объектом другому коду. Редко, когда вы на авито продаете вещи, чтобы заработать. Вы их продаете, чтобы от лишнего избавиться и дать их тем, кому они нужны, особой выгоды не ожидая. Вот здесь примерно это и должно происходить.
В реальности все немного сложнее и всегда будут исключения, но просто хочу обратить внимание, что мув семантика - это в первую очередь про передачу владения объектом и только потом уже оптимизация.
Think logically. Stay cool.
#cppcore #cpp11
1👍24❤8🔥5😎4