А был ли мальчик? (помянем плюсы)

User avatar
M. Ridcully
Уже с Приветом
Posts: 12017
Joined: 08 Sep 2006 20:07
Location: Силиконка

Re: А был ли мальчик? (помянем плюсы)

Post by M. Ridcully »

ksi wrote: 25 Jan 2018 05:37 А можно unrelated вопрос? У меня была такая ситуация (я не программист в чистом виде, поэтому сорри, если чего не понимаю): мне нужно создать в памяти копии какого объекта. В большом количестве, сотни тысяч или миллионы потенциально. Поинтеры на новые объекты куда-то положить, ну в массив например. Есть какой-нибудь трюк чтобы это сделать наиболее быстро или ничего кроме цикла из memcpy нельзя придумать?
Я бы в первую очередь задумался, точно ли мне нужно, одновременно / в каждый момент времени, держать все эти миллионы объектов? Нельзя ли свести к эквивалентной задаче, но чтобы держать миллионы PODs, последовательно расположенных в памяти один за другим, безо всяких указателей?
ksi wrote: 25 Jan 2018 05:37 И второй вопрос - а можно это как-то multithreaded или memory manager все равно не способен параллельно выделять память? Или нужен специальный memory manager, которые могут это поддержать? Такие есть?
На некоторых задачах кастомный аллокатор памяти довольно сильно может помочь. Как напишете, так и будет выделять, и параллельно тоже.
Мир Украине. Свободу России.
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Prosche wrote: 25 Jan 2018 03:03
AndreyT wrote: 25 Jan 2018 02:45 А если подумать головой, то для объектов, владеющих ресурсами, из соображений эффективности разумнее по возможности реализовывать move через swap - так чтобы moved-from объект в случае присваивания в него мог воспользоваться уже имеющимися ресурсами, вместо того, чтобы выделять с нуля новые.
Что за бред?
Вам стоит воспользоваться вашим же советом про голову. Во-первых, с какой стати кто-то передавая объект по && будет ожидать, что в нем появятся какие-то ресурсы. :%)
Что за чушь вы несете? Остались ли/появились ли в moved from объекте ресурсы или нет - НЕ является информацией, которая каким-то образом видна снаружи. Поэтому откуда вы взяли, что кто-то будет что-то "ожидать" - мне не ясно. Такого здесь никто не говорил - это вы сами придумали. Снаружи об этом никто ничего не знает и знать не должен. Для наружных наблюдателей moved from объект находится в некотором условно валидном состоянии, которому, например, можно присваивать - не более. Осталось ли у него что-то внутри - никого снаружи не касается.

Знание о том, сохранились ли/появились ли в объекте ресурсы - это исключительно скрытое внутреннее знание объекта и личное дело объекта. Если объект после move будет деструктирован, то он вместе с собой уничтожит и эти ресурсы. Если в объект будет выполнено присваивание, то он тогда проверит возможность переиспользования имеющихся ресурсов вместо выделения новых. Вот ради этого мы эти ресурсы и хранили (!).

На этом и основана идиома реализации move через swap между объектами-участниками. Для вас это новость? Как говорится, "век живи - век учись". Этой идиомой не надо злоупотреблять, ибо если возможности/необходимости вышеупомянутого переиспользования ресурсов нет, то swap будет менее эффективным подходом. Но тем не мнее...
Prosche wrote: 25 Jan 2018 03:03Свап применяют когда известно что объект будет разрушен и делают это по причине атомарности ака безопасности процедуры свапа, перед копированием.
Это утверждение звучит как что-то из высказываний армейских прапорщиков. "Молоток применяют для подбивания каблуков, а не для того, чтобы по танку им стучать". Из того, что вы видели применение swap только в copy-and-swap идиоме совсем не следует, что swap больше нигде применять нельзя.

А вот откуда вы взяли "атомарность" swap вообще не ясно. В copy-and-swap идиоме swap применяют из-за того, что он не бросает исключений. Никакой "атомарности" у него нет в принципе.
Prosche wrote: 25 Jan 2018 03:03Во-вторых, раз уж вы заговорили о быстродействии, то опять же подумайте, что быстрее, дефолт конструктор + свап или ассайнмент поинтера ресурсов и обнуление поинтера отдающего.
Вот поэтому я и сказал - включать мозги.

Давайте на примере `std::vector<int> a, b;` рассмотрим пример перемещающей операции присваивания `a = std::move(b);`. (Заранее скажу, что я не буду следить за exception safety, ибо речь не о том. Все эти способы допускают exception safe реализации без изменения их относительных эффективностей.)

---

Сначала для случая, когда объект `b` затем сразу деструктируется:

Обычный move с немедленным освобождением ресурсов
Move-присваивание:
1. Освобождаем память `a`
2. Копируем поля из `b` в `a`, обнуляем поля `b`
Move-присваивание закончилось
3. Деструктируем `b` (почти ничего не происходит, ибо `b` "пуст")

Мove через swap
Move-присваивание:
1. Oбмениваем местами поля `a` и `b`
Move-присваивание закончилось
2. Деструктируем `b` (при этом освобождается память, пришедшая в `b` из `a`)

Никакой разницы в эффективности пары "move + деструкция" здесь нет и в помине. Только во втором случае прежние ресурсы `a` переходят к `b` и живут в общем случае дольше.

---

А теперь рассмотрим ситуацию, когда `b` не деструктируется, а переиспользуется: в него copy-присваивается новое значение, т.е. `a = std::move(b); b = c;`

Обычный move с немедленным освобождением ресурсов
Move-присваивание:
1. Освобождаем память `a`
2. Копируем поля из `b` в `a`, обнуляем поля `b`
Move-присваивание закончилось
Copy-присваивание:
4. Выделяем новую память в `b`
5. Копируем данные из `c` в `b`
Copy-присваивание закончилось

Мove через swap
Move-присваивание:
1. Oбмениваем местами поля `a` и `b`
Move-присваивание закончилось
Copy-присваивание:
2. Проверяем, достаточно ли capacity в `b` для хранения `c`. Если достаточно, то просто копируем данные. Все.
3. Иначе, освобождаем память `b`
4. Выделяем новую память в `b`
5. Копируем данные из `c` в `b`
Copy-присваивание закончилось

Опять, эффективность получилась одинаковой за исключением случая "удачи" на шаге 2 в варианте "move через swap", в каковом случае этот вариант будет намного эффективнее. Вот ради этой возможной удачи мы и не спешим освобождать ресурсы, а позволяем им еще немножко пожить в `b`. Вдруг пригодятся?

---

Так понятнее?

Тонкостью в этом случае является ситуация `std::vector<SomeComplicatedClass>` и момент деструкции элементов вектора имеет для программы какое-то важное значение, т.е. задержка деструкции в этом случае будет нежелательной. Но на само деле потенциальная эффективность варианта "Мove через swap" держится лишь на экономии на тяжелых операциях перераспределения сырой памяти. Деструкцию элементов массива в обоих случаях можно делать сразу, т.е. этот момент - легко решаем.
Last edited by AndreyT on 25 Jan 2018 08:17, edited 3 times in total.
Best regards,
Андрей
User avatar
M. Ridcully
Уже с Приветом
Posts: 12017
Joined: 08 Sep 2006 20:07
Location: Силиконка

Re: А был ли мальчик? (помянем плюсы)

Post by M. Ridcully »

AndreyT wrote: 25 Jan 2018 02:45
M. Ridcully wrote: 24 Jan 2018 04:51 А компилятор как-то енфорсит неиспользование объекта, из которого сделали move?
С чего бы это вдруг он будет его энфорсить?...
До меня потихоньку доходит.
Выглядит как достаточно кастомная операция, семантика которой определяется индивидуально, каждым классом, который это поддерживает.
Видимо, посчитали, что это достаточно важный частный случай и решили создать инфраструктуру в стандартной библиотеке для возможной оптимизации для объектов, которые это поддерживают.

Теперь осталось допереть, как это связано с rvalue references...
Мир Украине. Свободу России.
User avatar
Мальчик-Одуванчик
Уже с Приветом
Posts: 15526
Joined: 27 Sep 2007 22:53

Re: А был ли мальчик? (помянем плюсы)

Post by Мальчик-Одуванчик »

ksi wrote: 25 Jan 2018 05:37 И второй вопрос - а можно это как-то multithreaded или memory manager все равно не способен параллельно выделять память? Или нужен специальный memory manager, которые могут это поддержать? Такие есть?
Как вариант сделай свой аллокатор и используй размещающую форму оператоа new
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

partner_ca wrote: 25 Jan 2018 08:19
M. Ridcully wrote: 25 Jan 2018 08:10 Теперь осталось допереть, как это связано с rvalue references...
По сути rvalue references - это обычная неконстантная ссылка, которая может ссылаться на временный объкт.
Совершенно верно. "rvalue references - это обычная неконстантная ссылка, которая может ссылаться на временный объект." Плюс набор правил, которые говорят, как эта ссылка ведет себя в overload resolution. Плюс набор правил type deduction в шаблонах. Плюс набор правил reference collapsing. И может еще чего-то...

Сама же move semantics реализуется чисто руками - на уровне библиотечного или пользовательского кода. То есть на уровне core language почти никакой move semantics нет. Ну разве что кроме таких мелочей, как использование move для аргумента `return`.

То есть формально мы реализуем move semantics на основе rvalue references чисто потому, что они каким-то чудом оказались хорошо подходящими для этих целей. (На самом же деле никакого чуда тут нет - rvalue references разрабатывались с оглядкой на move semantics.)
Last edited by AndreyT on 25 Jan 2018 09:30, edited 1 time in total.
Best regards,
Андрей
User avatar
Мальчик-Одуванчик
Уже с Приветом
Posts: 15526
Joined: 27 Sep 2007 22:53

Re: А был ли мальчик? (помянем плюсы)

Post by Мальчик-Одуванчик »

partner_ca wrote: 25 Jan 2018 08:19
M. Ridcully wrote: 25 Jan 2018 08:10 Теперь осталось допереть, как это связано с rvalue references...
По сути rvalue references - это обычная неконстантная ссылка, которая может ссылаться на временный объкт.
Подробное введение в rvalue-ссылки для тех, кому не хватило краткого
https://habrahabr.ru/post/322132/
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

partner_ca wrote: 25 Jan 2018 08:32
AndreyT wrote: 25 Jan 2018 08:22 То есть на уровне core language никакой move semantics нет. `return`.
Как посмотреть.
По умолчанию компилятор создает Мove Constructor и Move Assignment Operator
Ну, во-первых, термина "создает" в языке нет. Компилятор объявляет Мove Constructor и Move Assignment Operator. А вот определяет (фактически "создает") он их только в том случае, если они используются в программе.

Во-вторых, как вы сами понимаете, сам компилятор может только рекурсивно делегировать вызовы Мove Constructor и Move Assignment Operator членам класса. Рекурсия заканчивается либо на фундаментальных типах, для которых move тривиален (т.е. move нет вообще, move = copy), либо на типах с рукописными Мove Constructor и Move Assignment Operator.

То есть нетривиальная move semantics все равно реализуется "руками". Все остальное - концептуальные сущности.
Best regards,
Андрей
ksi
Уже с Приветом
Posts: 10061
Joined: 20 May 1999 09:01

Re: А был ли мальчик? (помянем плюсы)

Post by ksi »

partner_ca wrote: 25 Jan 2018 07:57
ksi wrote: 25 Jan 2018 06:16 Ну произвольная структура какая-то, инстанс какого-то класса. Нважно С или С++. Такая задача, неважно откуда она возникает, это специфическое применения. Потом эти копии будут модифицироваться, слега, ну какое-то поле будет подправлено. Мой опыт, что модификация занимает в сотни раз меньше времени, чем отведение памяти под объект, поэтому и вопрос. Надо сэкономить на копировании.
Быстрее вектора вряд ли получится.
Например:

Code: Select all

MyObject obj;
std::vector<MyObject> v(1000000, &obj);
А память под вектор выделяется одним куском или она может быть фрагментирована? Это можно как-то эмулировать в С стиле: я имею в виду, что если просто звать malloc (1000000*sizeof (MyObject)) то такого куска памяти подряд может просто не быть. У вектора есть внутренняя поддержка для таких ситуаций?
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

AndreyT wrote: 25 Jan 2018 08:05
Что за чушь вы несете? Остались ли/появились ли в moved from объекте ресурсы или нет - НЕ является информацией, которая каким-то образом видна снаружи. Поэтому откуда вы взяли, что кто-то будет что-то "ожидать" - мне не ясно. Такого здесь никто не говорил - это вы сами придумали. Снаружи об этом никто ничего не знает и знать не должен. Для наружных наблюдателей moved from объект находится в некотором условно валидном состоянии, которому, например, можно присваивать - не более. Осталось ли у него что-то внутри - никого снаружи не касается.

Знание о том, сохранились ли/появились ли в объекте ресурсы - это исключительно скрытое внутреннее знание объекта и личное дело объекта. Если объект после move будет деструктирован, то он вместе с собой уничтожит и эти ресурсы. Если в объект будет выполнено присваивание, то он тогда проверит возможность переиспользования имеющихся ресурсов вместо выделения новых. Вот ради этого мы эти ресурсы и хранили (!).

На этом и основана идиома реализации move через swap между объектами-участниками. Для вас это новость? Как говорится, "век живи - век учись". Этой идиомой не надо злоупотреблять, ибо если возможности/необходимости вышеупомянутого переиспользования ресурсов нет, то swap будет менее эффективным подходом. Но тем не мнее...
Мув был придуман для увеличения быстродействия. Вы же предлагаете всю идею похерить используя внутри свап. Зачем вам тогда вообще мув? Имплементируйте свап в вашем классе и будет вам счастье.
AndreyT wrote: А вот откуда вы взяли "атомарность" swap вообще не ясно. В copy-and-swap идиоме swap применяют из-за того, что он не бросает исключений. Никакой "атомарности" у него нет в принципе.
Я не говорил, что свап атомарная операция, а о том, что в отличие от копирования поэлементно и при возникновении исключения мы не получим наполовину скопированный объект.

AndreyT wrote: Вот поэтому я и сказал - включать мозги.
...
Вашу портянку позволю себе немного сократить и убрать лишнюю воду. На том же примере векторов а и в
Мув конструктор:
а(а&& _a) :
ref_(_a.ref)
count_(_a.count)
{
_a.ref_ = null;
_a.count_ = 0;
}
идея со свапом
а(а& _a) : a()
{
swap(_a);
// which
// swap(a& _a)
// {
// swap(ref_, a.ref_);
// swap(count_.a.count_);
// }
}
Внимание вопрос, что будет работать быстрее?
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Prosche wrote: 25 Jan 2018 14:41 Мув был придуман для увеличения быстродействия. Вы же предлагаете всю идею похерить используя внутри свап.
??? Я вам детально расписал пример гигантского фундаментального увеличения быстродействия, полученного именно за счет того, что move реализован через swap.
Prosche wrote: 25 Jan 2018 14:41Зачем вам тогда вообще мув? Имплементируйте свап в вашем классе и будет вам счастье.
Вы, я вижу, совсем "не рубите". Для того, чтобы этот swap натурально работал в move semantics, он должен вызываться из "перемещающих" special member functions. А в общем и целом - да, если бы move semantics сводился только к ручным вызовам, то во многих случаях было бы именно так, как вы сказали "Мув не нежен. Имплементируйте свап в вашем классе и будет вам счастье." Но, к сожалению, полноценная move semantics одними "ручными вызовами" не реализуется.
Prosche wrote: 25 Jan 2018 14:41Я не говорил, что свап атомарная операция, а о том, что в отличие от копирования поэлементно и при возникновении исключения мы не получим наполовину скопированный объект.
Во-первых - говорил. Во-вторых, это прекрасно, но совершенно ортогонально рассматриваемому вопросу.
Prosche wrote: 25 Jan 2018 14:41 Вашу портянку позволю себе немного сократить и убрать лишнюю воду. На том же примере векторов а и в
Мув конструктор:
а(а&& _a) :
ref_(_a.ref)
count_(_a.count)
{
_a.ref_ = null;
_a.count_ = 0;
}
идея со свапом
а(а& _a) : a()
{
swap(_a);
// which
// swap(a& _a)
// {
// swap(ref_, a.ref_);
// swap(count_.a.count_);
// }
}
Внимание вопрос, что будет работать быстрее?
Что за отвязнейшая чушь?

Во-первых, для демонстрации цели swap, как было ясно сказано, нужен именно move assignment operator, чтобы в левой части были уже существующие ресурсы, которые мы и пытаемся сохранить (!). Во-вторых, я ясно сказал, что речь идет об объектах эксклюзивно владеющих дорогими ресурсами типа `std::vector`. Никаких тупых подтасовок с `ref`, `new[]` пожалуйста. В-третьих - даже если где-то как-то вариант со swap окажется медленнее одностороннего присваивания, это делается ради гигантского выигрыша (!!) в последующих переиспользований этих ресурсов. Ваше "быстрее" (даже если оно существует) никого не интересует, ибо мое "быстрее" на два, три, сто порядков больше. Так понятнее?

Я вижу, "сокращение портянки" не сработало :) Чтобы мы тут без толку не теряли время сейчас и в будущем: мои "портянки" так ценятся именно за то, что изначально высушены до абсолютно дистиллированной сути, специально подогнанной под потребности конкретного обучаемого. Поэтому "сокращать" - запрещаю. Сейчас - назад к оригинальной "портянке" - работать так, как принято работать с моими постами: в тишине орехового кабинета читать и перечитывать до просветления обрамленную в стекло и раму красного дерева распечатку, стараясь сдержать дрожь в руках, учащенное сердцебиение и нестерпимое желание обзванивать всех друзей и знакомых с воплями "OMG! OMG! OMG! AndreyT мне ответил в интернете" :) Что-то будет непонятно - спрашивайте по сути, а не какими-то бессмысленными вбросами, дымовыми завесами и попытками "перевода стрелок".
Last edited by AndreyT on 25 Jan 2018 16:39, edited 2 times in total.
Best regards,
Андрей
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

Хорошо, хотите натягивать мув на свап, не смею вам мешать. Я сделал все что мог.
Хотите думать, что мув придуман чтобы сохранять дорогие ресурсы, барабан вам на шею. Будете сидеть в тишине вашего кабинета, попробуйте подумать почему именно мув, такое странное название, в словарик там посмотрите. Может быть осознаете простую истину, что идея мув в мув, взять справа и передвинуть влево, а совсем не сохранить "дорогие ресурсы".
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Ну что ж, я сделал все, что мог в данной непростой ситуации... Но, как говорится, этим, толкующим назначение свойств языка "по названию" и "со словариком" :) втолковывать что-то бывает бесполезно.

P.S. Что интересно, довольно часто встречаю на просторах интернета студентов, пытающихся "воевать" с устоявшейся С/С++ терминологией через Оксфордский словарь. Совсем недавно один пытался толковать слова "shall" и "should", не веря в мое объяснение их значения в тексте стандартных документов. Особенно это любят в русскоязычной среде, как ни парадоксально.
Best regards,
Андрей
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

И не говорите. Я вот все больше убеждаюсь, что у людей отсутствует элементарный каммон сенс, пытаются рассуждать о высоких материях, не понимая элементарного. Огромное число диванных теоретиков в любой области. А ты потом, после них, смотришь на код и пытаешься вкурить, почему передвинув маленький массив из а на место огромного в б, последний обнаруживается в а. Теперь вот буду знать, поработали спасатели "дорогих ресурсов" :)
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Prosche wrote: 25 Jan 2018 17:21 Огромное число диванных теоретиков в любой области. А ты потом, после них, смотришь на код и пытаешься вкурить, почему передвинув маленький массив из а на место огромного в б, последний обнаруживается в а.
О, это целый пласт!

Сразу приходит в голову такой популярный вопрос на Интернете у разработчиков of Bangalor persuasion. Типа: "Я уничтожил свой массив через `delete[]`, а потом заглянул в якобы "уничтоженную" память через этот же указатель, а мои данные там все лежат нетронутые. Как так? Почему? Я же сказал - "уничтожить", а они все равно там!" (Вариации: возвращение указателя на локальный объект и т.д. и т.п. Отдельный плюс: ссылка на значение слова `delete` в Оксфордском словаре. )

И потом приходится вдалбливать нерадивому студенту, что совать свой нос в освобожденную память не нужно. И что то, что там "осталось" или "не осталось" значения не имеет и его касаться/интересовать вообще не должно. Память освобождена - это все, что ему нужно знать. Его массив был концептуально уничтожен.

Но он, как правило, услышав слово "концептуально", начинает лезть в бутылку: первое время обзывает всех "теоретиками", ибо он, жесткий "практик", уже доказал нам всем, что "на практике" ничего не уничтожилось и то он "на практике" этим давно пользуется и у него "на практике" все работает. И т.д. и т.п. Количество времени, требуемое на пробуждение вменяемости у таких "практиков" варьируется в широких пределах. И успех не гарантирован.

Вот, я смотрю, и у вас что-то наблюдается нестерпимое желание совать в свой нос в недра moved from объекта и разглядывать то, что в нем "обнаруживается"... И понять тот факт, что в рамках move semantics это вас вообще не касается и интересовать совершенно не должно, вам похоже, пока не по силам...
Last edited by AndreyT on 25 Jan 2018 19:01, edited 2 times in total.
Best regards,
Андрей
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

когда у вас массив на пару гигов, который вы заменили другим, как вы считаете, на самом деле никуда не денется, а продолжит отжирать память, то вы очень быстро и "под капот" залезете, и все детали имплементации узнаете, и еще и толстый пучок диарейных лучей в направлении автора пошлете.
User avatar
Medium-rare
Уже с Приветом
Posts: 9195
Joined: 04 Mar 2011 03:04
Location: SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Medium-rare »

AndreyT wrote: 23 Jan 2018 19:32 Во-первых, львиная часть идеи шаблонного программирования как раз и заключается в том, что шаблонный класс может содержать операции, неприменимые для некоторых типов шаблонного аргумента, но это нам никак не должно мешать, пока мы не пытаемся эти операции использовать (инстанциировать).
Ну, на практике, когда пользователь моего компонента не может применить стандартную операцию над контейнером данных, полученных от него, можно, конечно, объяснить, что ему необязательно ожидать наличие такой операции в данном конкретном случае. Но обычно они продолжают возмущаться. :)
Во-вторых, ожидание доступности всех операций контейнера - это некий С++98-стиль мышления. В С++98 первичен был сам контейнер: доминировали жесткие требования контейнера, а элемент вынужден был волей-неволей под них подстраиваться, даже если это больше нигде никому не было нужно. И это, вообще-то, плохо, ибо с точки зрения здравого смысла должно быть как раз таки наоборот - свойства элемента контейнера должны быть первичны. И они должны прозрачно проноситься (форвардиться) сквозь контейнер в окружающий мир, также как это работает с "рукописными" голыми массивами, списками и т.п.
Duly noted. Так и буду отпинывать недовольных моими контейнерами.
Причем в С++98 такое жесткое доминирование правил контейнера сделали отнюдь не потому что это считалось "правильным", а просто потому, что в языке не было адекватных средств для того, чтобы сделать по-другому. Это стало возможно только в С++11. Но даже и в С++98 допустимые операции над контейнером зависели от определенных свойств элемента, хотя бы на интерфейсном уровне - например, возможность или возможность конструкции по умолчанию для элемента влияла на большое количество возможностей вызова методов контейнера.
static_assert почаще применять для устранения недопониманий... Как в свой код поставить, понятно. А если хочется внятно сообщить о неправильном случае использования стандартного контейнера для конкретного типа?
... and even then it's rare that you'll be going there...
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Prosche wrote: 25 Jan 2018 18:05 когда у вас массив на пару гигов, который вы заменили другим, как вы считаете, на самом деле никуда не денется, а продолжит отжирать память, то вы очень быстро и "под капот" залезете, и все детали имплементации узнаете, и еще и толстый пучок диарейных лучей в направлении автора пошлете.
Мне надоело рассказывать "сказку про белого бычка" снова и снова.

Всем прекрасно понятно, что в "среднестатистических" приложениях в 99 случаях из 100 move делается из expiring объекта. То есть moved from объект будет уничтожен практически сразу после того, как из него сделали move. В такой ситуации разница во времени жизни ресурсов в любой реализации move будет сводиться к жалкому десятку тактов процессора. Мove-присваивание, реализованное через swap, тоже с радостью освободит вашу "пару гигов" при деструкции правой части. Так что не надо пугать людей: никто не будет "продолжать отжирать память".

В тех же ситуациях, когда правая часть продолжает долго жить и после move, этот вопрос уже переходит в совсем другую плоскость. Раз он продолжает долго жить, то значит он кому-то нужен? Может быть это и хорошо, что он продолжает отжирать "пару гигов"? Может в него сейчас эти самые "пару гигов" и захотят записать, а он как раз к этому готов?

На такие вопросы не существует ответа вне конкретных контекстов.

И даже в тех ситуациях, когда эти "пару гигов" действительно никому не нужны, вопрос на самом деле уже впрямую и не относится к move semantics. Это на самом деле та же извечная проблема как, например, в `std::vector`, который исторически накопил capacity в ваши "два гига", но сейчас имеет size в какие-то жалкие три байта. Что делать? Может вызывать shrink_to_fit? А может погодить - вдруг сейчас "два гига" таки сразу понадобятся?

Повторюсь: на такие вопросы не существует ответа вне конкретных контекстов.
Best regards,
Андрей
User avatar
Prosche
Уже с Приветом
Posts: 8090
Joined: 08 Nov 2004 12:24
Location: GA

Re: А был ли мальчик? (помянем плюсы)

Post by Prosche »

AndreyT wrote: 25 Jan 2018 18:57 В тех же ситуациях, когда правая часть продолжает долго жить и после move, этот вопрос уже переходит в совсем другую плоскость. Раз он продолжает долго жить, то значит он кому-то нужен? Может быть это и хорошо, что он продолжает отжирать "пару гигов"? Может в него сейчас эти самые "пару гигов" и захотят записать, а он как раз к этому готов?

На такие вопросы не существует ответа вне конкретных контекстов.
Повторюсь: на такие вопросы не существует ответа вне конкретных контекстов.
Вы начинаете повторяться.
Ответ безусловно существует. И он тривиален. В таком случае надо использовать свап. И мув тут совершенно не при делах.
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Re: А был ли мальчик? (помянем плюсы)

Post by AndreyT »

Prosche wrote: 25 Jan 2018 19:09 Ответ безусловно существует. И он тривиален. В таком случае надо использовать свап. И мув тут совершенно не при делах.
Move semantics для класса в С++ - это не более чем набор special member sunctions (move constructor и move assignment operator), которые служат в качестве интерфейса между рукописным кодом и ядром языка. А также простое "джентельменское соглашение": левая часть должна получить значение правой части, а правая часть должна получить некоторое неспецифицированное, но валидное состояние. Никаких других "надо" или "не надо" тут нет.

Классическая copy semantics является примером move semantics. Swap semantics тоже является примером move semantics. Можно придумать еще множество разнообразных примеров move semantics. С чего вы взяли, что ваши верования являются Единственным Истинным Способом реализации move semantics мне в упор не ясно.
Best regards,
Андрей
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

ksi wrote: 25 Jan 2018 05:37 А можно unrelated вопрос? У меня была такая ситуация (я не программист в чистом виде, поэтому сорри, если чего не понимаю): мне нужно создать в памяти копии какого объекта. В большом количестве, сотни тысяч или миллионы потенциально. Поинтеры на новые объекты куда-то положить, ну в массив например. Есть какой-нибудь трюк чтобы это сделать наиболее быстро или ничего кроме цикла из memcpy нельзя придумать? И второй вопрос - а можно это как-то multithreaded или memory manager все равно не способен параллельно выделять память? Или нужен специальный memory manager, которые могут это поддержать? Такие есть?
Если реально столько много их то можно было бы подумать не о куче а о глобальной памяти.
Если вы знаете верхний предел то можно выделить сразу большой кусок.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко
Pantigalt
Уже с Приветом
Posts: 803
Joined: 24 Jan 2007 07:32
Location: Сергели->Новосибирск->SFBA->Новосибирск->Москва->NY->SFBA

Re: А был ли мальчик? (помянем плюсы)

Post by Pantigalt »

ksi wrote: 25 Jan 2018 06:16 Ну произвольная структура какая-то, инстанс какого-то класса. Нважно С или С++. Такая задача, неважно откуда она возникает, это специфическое применения. Потом эти копии будут модифицироваться, слега, ну какое-то поле будет подправлено. Мой опыт, что модификация занимает в сотни раз меньше времени, чем отведение памяти под объект, поэтому и вопрос. Надо сэкономить на копировании.
У вас классическая задача COW - copy on write. Погуглите на эту тему.
Насколько я помню Windows это вовсю использует для работы с памятью.
Спи быстрее, твоя подушка нужна другому. Copyright Зощенко

Return to “Вопросы и новости IT”