Здравствуйте, chaotic-kotik, Вы писали:
CK>если у тебя достаточно простые обработчики, то передача параметров это узкое место, для серверов это зачастую так, я может пишу на каком-нибудь DPDK и у меня эти коллбэки миллионы раз в секунду вызываются, какие к черту RTTI с копированием?
CK>про твой MoveObject не будет знать стандартная библиотека, соотв. многие оптимизации не смогут быть использованы
Кстати о мув-семантике, в С++ она на порядок сложнее, чем в Расте. Потому что С++ предполагает, что объект может косвенно содержать ссылку на себя, которую надо править после мува, а Раст предполагает, что не может, и поэтому объекты можно спокойно двигать по памяти, не нарушая их валидность. То есть в Расте вектор любого объекта можно увеличивать тупым realloc, например.
За годы программирования на С++ мне ни разу не понадобились объекты с нетривиальным перемещением. Вектора, строки, хеш-таблицы, юник_птр, шаред_птр — все они могут быть спокойно реаллоцированы без потери смысла. В тех же случаях, когда объекты действительно нужны на своём месте, потому что на них кто-то ссылается из совсем другого места кода, уже приходится отдельно заводить либо юники, либо вектор ЭН-элементных нереаллоцирующих векторов.
Так вот, а кому вообще сдалась сложная С++-ная мув-семантика?
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, lpd, Вы писали:
lpd>>Можно подумать у программистов других проблем нет, чем возиться с шаблонами ради 2% скорости программы.
TB>Двух процентов, лол, правда? TB>Не говоря уже про кучу других применений шаблонов, типа проверки безопасности алгоритма длдя каждого типа.
А ты измерял, сравнивал? Я думаю в большинстве случаев меньше 2% будет в сумме, даже если ты все классы сделаешь шаблонными и rvalue.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Здравствуйте, T4r4sB, Вы писали:
TB>Так вот, а кому вообще сдалась сложная С++-ная мув-семантика?
Это попытка исправить родовую травму с++ — копирование по умолчанию.
Вот только копирование в 99% не нужно — по умолчанию нужна передача по ссылке (константной). В общем аналогично, как это сделано в С#.
Достаточно было изменить дефолтное поведение + стандартизировать оптимизации при возврате/передаче и всё это обернуть в параметры компилятора для нового/старого с++ и это была бы бомба.
Но это бы сломало уже написанные программы + комитет до усрачки боится перемен (либо кто-то там сознательно старается "прикопать" с++).
Потому имеем что имеем.
Здравствуйте, AeroSun, Вы писали:
AS>Здравствуйте, T4r4sB, Вы писали:
TB>>Так вот, а кому вообще сдалась сложная С++-ная мув-семантика?
AS>Это попытка исправить родовую травму с++ — копирование по умолчанию. AS>[..] AS>Потому имеем что имеем.
Да move и не такой сложный в C++. Просто нужно привыкнуть, что сам std::move ничего никуда не переносит, а лишь меняет тип объекта. Переносят же соответствующий конструктор и оператор присваивания. Еще помнить, что любой метод, который не является noexcept, существенно замедляет перенос, ибо происходит за кулисами копирование объекта на всякий пожарный. Вроде бы ничего не забыл?
Однако, в сравнении с Rust это конечно все слишком сложно. Уверен, что 99% растоманов даже не задумываются о том, как работает move.
Здравствуйте, dsorokin, Вы писали:
D>Да move и не такой сложный в C++.
Сложный. Вместо realloc приходится разбирать несколько случаев: у объектов есть ноексептнутый кастомный мув, у объектов запрещён кастомный мув, у объектов есть ексептнутый кастомный мув, итд.
Здравствуйте, T4r4sB, Вы писали:
TB>Сложный. Вместо realloc приходится разбирать несколько случаев: у объектов есть ноексептнутый кастомный мув, у объектов запрещён кастомный мув, у объектов есть ексептнутый кастомный мув, итд.
какая связь между realloc и move семантикой в Rust?
Здравствуйте, chaotic-kotik, Вы писали:
CK>Здравствуйте, T4r4sB, Вы писали:
TB>>Сложный. Вместо realloc приходится разбирать несколько случаев: у объектов есть ноексептнутый кастомный мув, у объектов запрещён кастомный мув, у объектов есть ексептнутый кастомный мув, итд.
CK>какая связь между realloc и move семантикой в Rust?
В Rust можно тупо вызвать realloc для вектора любый объектов, в C++ нельзя.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Здравствуйте, T4r4sB, Вы писали:
TB>>В Rust можно тупо вызвать realloc для вектора любый объектов, в C++ нельзя.
CK>таки для любого вектора? и при чем здесь move семантика?
Да, для любого.
Тот факт, что объекты С++ имеют право иметь нетривиальный T(T&&), тоже является частью С++ной мув-семантики.
И это право нужно лишь в крайне редкий случаях, для остальных это адовый гемор.
Здравствуйте, T4r4sB, Вы писали:
TB>Да, для любого.
а если аллокатор не может реаллокейтнуть буфер по какой-либо причине?
TB>Тот факт, что объекты С++ имеют право иметь нетривиальный T(T&&), тоже является частью С++ной мув-семантики. TB>И это право нужно лишь в крайне редкий случаях, для остальных это адовый гемор.
гемор для кого? разработчику приложений на С++ практически никогда не нужно реализовывать T(T&&) самостоятельно, что ты имеешь ввиду?
я так и не понял при чем тут reallocate, если ты reallocate буфер в памяти, то тебе ничего мувать не нужно, у тебя остается тот же буфер, меняется только его размер
Здравствуйте, chaotic-kotik, Вы писали:
CK>а если аллокатор не может реаллокейтнуть буфер по какой-либо причине?
Это особая ситуация
CK>гемор для кого? разработчику приложений на С++ практически никогда не нужно реализовывать T(T&&) самостоятельно, что ты имеешь ввиду?
А если надо? Не перегрузил 100500 версий T(T&&) и дефолтное поведение будет либо некорректное, либо неоптимальное
CK>я так и не понял при чем тут reallocate, если ты reallocate буфер в памяти, то тебе ничего мувать не нужно, у тебя остается тот же буфер, меняется только его размер
Вообще-то буфер может переместиться. И если в буфере сидят С++-объекты, то после влобного побайтового перемещения объекты могут стать инвалидными.
TB>Это особая ситуация TB>Вообще-то буфер может переместиться. И если в буфере сидят С++-объекты, то после влобного побайтового перемещения объекты могут стать инвалидными.
все, я понял что ты называешь realloc совсем не тоже самое что я, я думал про сишный realloc, который делает совсем другое
CK>>гемор для кого? разработчику приложений на С++ практически никогда не нужно реализовывать T(T&&) самостоятельно, что ты имеешь ввиду? TB>А если надо? Не перегрузил 100500 версий T(T&&) и дефолтное поведение будет либо некорректное, либо неоптимальное
вообще, по хорошему тебе ничего не нужно перегружать, если ты пишешь кастомные T(T&&) и operator = (T&&) то ты что-то делаешь не так скорее всего
само собой, если ты зачем то пишешь свой смарт поинтер, то тебе придется явно определить это (т.к. неявно они не будут определены, т.к. у тебя будут явный к-р и оператор присваивания), но пользователям уже не обязательно сильно в этом разбираться
Здравствуйте, chaotic-kotik, Вы писали:
CK>все, я понял что ты называешь realloc совсем не тоже самое что я, я думал про сишный realloc, который делает совсем другое
Я про сишный и говорю. Он может переместить буфер. Для С++-объекта это проблема. Для Раст-объекта — нет. В чём непонятки-то?
CK>вообще, по хорошему тебе ничего не нужно перегружать, если ты пишешь кастомные T(T&&) и operator = (T&&) то ты что-то делаешь не так скорее всего
Это всего лишь значит, что я пишу объект, управляющий ресурсом. В С++03 надо было перегрузить 4 хрени: T(), T(T&), =(T&), ~T(). В ++20 я боюсь их считать.
На деле из них большая часть лишние. Всего-то надо было добавить требование тривиальной реллоцируемости.
Здравствуйте, lpd, Вы писали:
lpd>Использовать move-семантику для оптимизации, особенно везде — это вообще дно. Оптимизировать нужно только узкие места программы. Передача аргументов не играет решающей роли в быстродействии. ЕСЛИ у тебя большие объекты копируются в критичном коде при легковесной обработке их, можно легко сделать только для них метод MoveObject(), а не перегружать язык новыми типами &&(имеющими только академический интерес) с запутанным синтаксисом.
Когда ты с таким ужасом говоришь об оптимизации, то естественно представляется собой классическая картинка, когда ради увеличения быстродействия заменяют короткий и простой код на какой-то большой и сложный, но зато быстрым. Однако в случае использования семантики перемещения ничего подобного не наблюдается. Даже в том случае, если тебе надо руками реализовывать соответствующие операторы для какого-то сложного объекта, то это лишь означает, что раньше у тебя там реализовывалась точно такая же сложная логика для оператора копирования. Т.е. в реальности никакого усложнения или увеличения прикладного кода от использования семантики перемещения нет. И использовать её повсеместно более чем правильно.
И да, сама по себе семантика перемещения нужна в языке в первую очередь не ради оптимизации, а для возможности реализации полноценного RAII и соответствующего управления ресурсами. Без неё просто физически невозможно написать соответствующие умные указатели и т.п. Однако побочно её использование частенько приводит и к существенному (например в случае использования в проекте различных контейнеров) увеличению быстродействия. И я не вижу ни единой причины, почему надо от него отказываться.
lpd>Да я и не против шаблонов самих по себе, какими они были в классическом С++. Я считаю тупиковым путь развития С++, потому что добавляются только представляющие узкий интерес только для специалистов языкам фичи, вместо добавления языку новых возможностей общего назначения.
Что такое "возможности общего назначения"? )
lpd>И на Java/C# уже давно пишется гораздо больше.
Ну во-первых только на Java (см. например здесь https://madnight.github.io/githut/#/pull_requests/2020/1), а во-вторых если следовать этой логике, то и Java давно пора выбросить на свалку, заменив везде на JS (см. на тот же график).
Здравствуйте, AeroSun, Вы писали:
TB>>Так вот, а кому вообще сдалась сложная С++-ная мув-семантика? AS>Это попытка исправить родовую травму с++ — копирование по умолчанию. AS>Вот только копирование в 99% не нужно — по умолчанию нужна передача по ссылке (константной). В общем аналогично, как это сделано в С#.
Что-то мне кажется, что ты путаешь модели памяти (в C# все объекты — это ссылки) и модели передачи параметров в функции в C#. )))
А так да, я тоже считаю, что передача по константной ссылке была бы правильным дефолтным способом передачи параметров в функцию. Исключительно ради того, чтобы писать меньше букв, т.к. этот способ встречается чаще передачи по значению или по мутабельной ссылки.
Однако к семантике перемещения это точно не имеет никакого отношения. Более того, это "ни к чему" не имеет отношения, потому как собственно и проблемы тут нет — язык спокойно поддерживает все возможные способы передачи данных и то, что самый популярный способ не является дефолтным — это всего лишь мелкое досадное неудобство, а не проблема, которую надо как-то решать.
AS>Достаточно было изменить дефолтное поведение + стандартизировать оптимизации при возврате/передаче и всё это обернуть в параметры компилятора для нового/старого с++ и это была бы бомба. AS>Но это бы сломало уже написанные программы + комитет до усрачки боится перемен (либо кто-то там сознательно старается "прикопать" с++). AS>Потому имеем что имеем.
И как это позволит написать например unique_ptr? )
Здравствуйте, T4r4sB, Вы писали:
TB>Я про сишный и говорю. Он может переместить буфер. Для С++-объекта это проблема. Для Раст-объекта — нет. В чём непонятки-то?
Ну вообще говоря оба языка системные и соответственно оба позволяют что угодно. Это я в том смысле, что и в Rust'е ты можешь с помощью unsafe создать любые ссылки самого на себя и т.п. )
TB>Это всего лишь значит, что я пишу объект, управляющий ресурсом. В С++03 надо было перегрузить 4 хрени: T(), T(T&), =(T&), ~T(). В ++20 я боюсь их считать. TB>На деле из них большая часть лишние. Всего-то надо было добавить требование тривиальной реллоцируемости.
Если мы говорим об объекте управляющем ресурсами, то при использование семантики перемещения тебе надо будет не добавить новый оператор, а заменить старый, т.к. оператор копирования скорее всего станет запрещённым.
Дополнительные перегрузки появляются в основном у всяких универсальных контейнеров (позволяющих грузить в себя данные множеством разных способов). Но такие вещи живут в первую очередь в базовых библиотеках, а не в прикладном коде.
Здравствуйте, T4r4sB, Вы писали:
CK>>все, я понял что ты называешь realloc совсем не тоже самое что я, я думал про сишный realloc, который делает совсем другое TB>Я про сишный и говорю. Он может переместить буфер. Для С++-объекта это проблема. Для Раст-объекта — нет. В чём непонятки-то?
я распарсил, терминология просто немного alien для тех кто не знает Rust
тут у с++ скорее другая проблема, в интерфейсе аллокатора в принципе нет realloc, который std::vector мог бы использовать например для POD объектов, вектор всегда копирует, хотя в linux сишный realloc реализован через mremap, что позволило бы вставлять POD-ы в вектор практически без копирования, насколько я помню folly::vector умеет так делать для POD-ов, лично мне большего и не нужно
но в принципе да, согласен с оригинальным сообщением, в плюсах реализовывать свой вектор довольно сложно из-за exception guarantees
CK>>вообще, по хорошему тебе ничего не нужно перегружать, если ты пишешь кастомные T(T&&) и operator = (T&&) то ты что-то делаешь не так скорее всего TB>Это всего лишь значит, что я пишу объект, управляющий ресурсом. В С++03 надо было перегрузить 4 хрени: T(), T(T&), =(T&), ~T(). В ++20 я боюсь их считать. TB>На деле из них большая часть лишние. Всего-то надо было добавить требование тривиальной реллоцируемости.
чем тебе unique_ptr для управления ресурсом не угодил? пользовательский код не должен, как правило, все это реализовывать, только если ты сам пишешь всякие библиотеки контейнеров, так что не такая уж это и проблема, тривиальная реаллоцируемость это хорошо конечно же
Здравствуйте, chaotic-kotik, Вы писали:
CK>я распарсил, терминология просто немного alien для тех кто не знает Rust
Я тоже не знаю Раст. Реаллок — это термин из древней сишки.
CK>тут у с++ скорее другая проблема, в интерфейсе аллокатора в принципе нет realloc, который std::vector мог бы использовать например для POD объектов, вектор всегда копирует, хотя в linux сишный realloc реализован через mremap, что позволило бы вставлять POD-ы в вектор практически без копирования, насколько я помню folly::vector умеет так делать для POD-ов, лично мне большего и не нужно
А для не-ПОДов? Не может, да? Жаль.
CK>чем тебе unique_ptr для управления ресурсом не угодил?
Ну для нереаллоцируемого неперемещаемого сгодится, но как-то криво тогда называть пторм то, что используется для другого.
CK>пользовательский код не должен, как правило, все это реализовывать, только если ты сам пишешь всякие библиотеки контейнеров, так что не такая уж это и проблема
А я не хочу быть прибитым гвоздями к мегабайтам СТЛ, например.
Здравствуйте, alex_public, Вы писали:
_>Ну вообще говоря оба языка системные и соответственно оба позволяют что угодно. Это я в том смысле, что и в Rust'е ты можешь с помощью unsafe создать любые ссылки самого на себя и т.п. )
При этом библиотека и компилятор вправе игнорить факт наличия таких ссылок и спокойно реаллоцировать такие объекты.
Здравствуйте, T4r4sB, Вы писали:
TB>Здравствуйте, chaotic-kotik, Вы писали:
CK>>я распарсил, терминология просто немного alien для тех кто не знает Rust
TB>Я тоже не знаю Раст. Реаллок — это термин из древней сишки.
Подозреваю, что тут получился какой-то глухой телефон.
У меня есть версия, что тут имелось в виду. В языке Rust для того, чтобы перенести объект (из стека в кучу, или из стека в замыкание, или с одного замыкания в другое замыкание) достаточно просто сделать две вещи: 1) тупо по-битово скопировать handle объекта (часто несколько байт); 2) а потом просто для старого объекта _не_ вызывать деструктор Drop, т.е. ничего не делать. Вот и все!
Я так понимаю, что "realloc" это как раз на тему перемещения.
Тем не менее, в Rust можно привязаться к адресу в памяти. Для этого есть std::pin, но честно говоря, я туда вот прямо сейчас единственный раз заглянул за года три только для того, чтобы написать этот комментарий