company_banner

Feature freeze C++20. Coroutines, Modules и прочее

    На днях прошла встреча международного комитета по стандартизации C++ в американском городе Кона. Это была не просто встреча, а feature freeze! Никакие серьёзные новые идеи больше не могут просачиваться в стандарт, остаётся лишь пара встреч на добавление предварительно одобренных вещей, исправление недочётов и устранение шероховатостей.

    Ожидать ли Модули и Корутины в C++20, будет ли там быстрая библиотека для форматирования вывода, сможет ли она работать с календарями, добавили ли std::stacktrace, начнёт ли компилятор сам вызывать std::move в ряде случаев, приняли ли std::flat_map? Всё это и многое другое ожидает вас под катом.



    Coroutines TS


    Самые жаркие дебаты разгорелись вокруг сопрограмм. Комитету предстояло рассмотреть три различных подхода к сопрограммам и решить, принимать в стандарт имеющиеся Coroutines TS, либо идти другим путём.

    Выбор был не простой, у каждого подхода есть свои минусы и плюсы:

    • N4775:
      • + нет ограничений на то, что сопрограммы должны быть описаны в заголовочном файле
      • нет строгих гарантий, что не произойдёт динамическая аллокация
      • ± не самый простой интерфейс (P1477R0 это исправляет)
      • страшненькие ключевые слова co_await и co_yield (предложение P1485R0 от РГ21 это исправляет)
      • + 3 года применяются на практике

    • P1063R2:
      • + нет динамических аллокаций
      • сопрограммы должны быть описаны в заголовочном файле или самим надо хитровыкручиваться с type erasure
      • ещё более страшные ключевые operator[<-] и [->]
      • нет рабочего прототипа
      • не самый простой интерфейс для создания асинхронных вещей

    • P1430R0:
      • + нет динамических аллокаций
      • сопрограммы должны быть описаны в заголовочном файле или самим надо хитро выкручиваться с type erasure
      • + нет страшных ключевых слов, всё гладенько
      • + пользователи корутин не видят страшных корутиновых внутренностей (даже не видят co_await аналогов, всё из коробки работает)
      • первое предложение, ни разу не обсуждалось, требует кучи доработок
      • невозможно реализовать на текущих технологиях (требуют поддержки структур динамического размера), требуют огромных трудозатрат для реализации
      • ± немного напоминают лапшу из callback


    После долгих дебатов сопрограммы были приняты в C++20 в том виде, в котором они были в Coroutines TS (с co_* префиксами и старыми точками кастомизации).

    Modules


    На обсуждение модулей повлиял один интересный документ с замерами производительности:
    P1441R0. Трактовать результаты можно по разному: от «существующие системы сборки и реализация модулей ещё недостаточно оптимизированы» до «модули плохо масштабируются с ростом сложности проекта».

    Помимо этого документа, комитет обсудил ряд небольших правок к текущим модулям. В итоге, спустя 15 лет обсуждения, прототипирования и экспериментов с внедрениями, модули были приняты в C++20.

    Format


    Good news everyone! Если не найдут фатальных недостатков в подгруппе Library, то в C++20 можно будет безопасно и очень быстро форматировать строки. Скажите «до свидания» std::ios, std::locale и прочим ужасам 90-х! Теперь Python подобный синтаксис для форматирования доступен из коробки в С++: P0645R5.

    Более того, было принято предложение на интеграцию нового форматирования и календарных времён P1361R0. Если всё пойдёт по плану, то и даты можно будет выводить по-человечески!

    Networking, Executors и Properties


    Executors являются важным кирпичиком для поддержки Networking в C++ из коробки. Для Executors нужны Properties — возможность модифицировать тип данных, в зависимости от параметра переданного на этапе компиляции, не меняя концепт типа.

    Properties описаны в P1393R0. Не смотря на то, что текста для включения в стандарт всего пара страничек, предложение вызвало бурные обсуждения. Предложение позволяет делать практически всесильные точки кастомизации, менять поведение любых функций, использующих Properties.

    В итоге решено было Properties включать в язык только в C++23, а соответственно и Executors, и Networking в C++20 не появятся.

    Прочее


    В черновик C++20 уже были внесены следующие изменения:

    • Структуры без конструкторов (агрегаты) теперь можно инициализировать, используя круглые скобки P0960. На практике это значит, что теперь std::make_shared, std::make_unique, std::*::emplace* будут корректно работать с агрегатами без ошибок компиляции
    • Были добавлены функции lerp для линейной интерполяции P0811
    • Добавлена возможность векторизировать алгоритмы стандартной библиотеки P1001
    • Методы std::span теперь возвращают беззнаковые типы (по аналогии со всей стандартной библиотекой) + была добавлена функция std::ssize для получения размера контейнера в виде знакового числа P1227
    • Unordered контейнеры научились искать значения, используя заранее посчитанный хеш P0920

    Много других вещей ожидают финального ревью в подгруппах Library и Core, для включения в C++20:

    • Эффективное ожидание на std::atomic; классы семафоров и барьеров P1135
    • std::flat_map P0429
    • std::flat_set P1222
    • std::function_ref P0792
    • constexpr для <cmath> и <cstdlib> P0533
    • std::ranges::to<любой-контейнер> для сохранения диапазона значений в контейнер P1206
    • Возможность эффективно извлекать строки из std::*stringstream и передавать во владение пользовательские строки P0408
    • Множественные правки для operator<=>, ranges, constexpr

    Заслуги РГ21


    В самый первый день за горячо любимое в Яндекс.Такси предложение на Stacktrace P0881R3 взялась подгруппа Core. Замечания по дизайну были дополнительно обсуждены в подгруппе LEWG, ещё раз проработаны в Core. В итоге в течении всей недели вносились правки и велись обсуждения. В черновик стандарта предложение ещё не включено, но должно оказаться в C++20 (если не найдут вдруг какой-то фатальный недостаток).

    До обсуждения нашей идеи P1485R0 на приведение ключевых слов для корутин дело не дошло.

    Также в SG1 Concurrency обсуждали идею concurrent unordered map P0652R2. Нас попросили перепроверить, что предложенное API позволяет избежать reader contention. Также сказали поисследовать concurrent unordered контейнеры, которые не имеют функции erase и не защищают значение контейнера от конкурентной модификации.

    Предложение от ZaMaZaN4iK на специализацию std::hash для различных классов стандартной библиотеки P1406R0 решено было сильно порезать. Комитет порекомендовал оставить специализации только для std::pair, std::tuple, std::array и std::basic_string от пользовательских аллокаторов.

    В подгруппе цифр SG6 обсудили механизм для взаимодействия различных новых классов чисел P0880R2. Предложение позволяет специализировать два шаблона, получить полный набор математических операций и сравнений для всех новых типов. По итогам обсуждения решили попробовать расширить механизм, чтобы им можно было пользоваться не только для нужд стандартной библиотеки, но и пользователи могли использовать эти операторы для своих типов.

    Также обсуждали наши незначительные предложения, включая feature testing macro P1424R0 и политики на их добавление в стандарт.

    Быстро обсудили нашу идею, позволяющую компилятору убирать лишние копирования R0889R1. Нам сказали продолжать работу в этом направлении и накидали примеров, которые не должны ломаться с новыми правилами.

    Вместо итогов


    C++20 будет так же разительно отличаться от C++17, как С++11 отличался от C++03. Огромное количество новых технологий и новых парадигм: Concepts, Contracts, Ranges, Modules, Coroutines, constexpr контейнеры и constexpr динамический полиморфизм, «ниблойды» и т. д.

    В скором времени мы, Рабочая Группа 21, отправим комментарии к черновику стандарта C++20. Поэтому, если у вас есть какая-то боль или вы не согласны с каким-то нововведением, пожалуйста, оставляйте свои мысли на этой странице.

    Следующее собрание международного комитета будет летом, на нём могут начать рассматривать нововведения для C++23. Если вы хотите что-то изменить в C++ или предложить свою идею, то всегда можете написать на https://stdcpp.ru/, где люди из РГ21 помогут вам донести ваши желания до комитета.

    Желаете поговорить с нами вживую? Скоро состоится открытая встреча РГ21, следите за анонсами на events.yandex.ru. Так же ищите нас на апрельской конференции C++ Russia в Москве.

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Что из C++20 вам больше всего пригодится в разработке?

    Яндекс
    572,00
    Как мы делаем Яндекс
    Поделиться публикацией

    Комментарии 206

      +4
      Clickhouse станет еще быстрее? ;)
      PS: go девелоперы всплакнули…
        +3
        Станет :) В стандарте немного подкрутили правила для типов и улучшили стандартную библиотеку. Так что компиляторы смогут оптимизировать ещё чуточку лучше.

        А вот GO разработчикам ещё рано плакать — корутины приняли, но в стандартной библиотеке нет готовых классов для их использования. Придётся первое время писать всё самим, так что C++ тут пока отстаёт от GO. (Но мне по секрету сказали, что постараются добавить необходимые классы в std::experimental в очень скором времени, ещё до C++23).
          –4
          Плакать скорее хочется от того, как жутко выглядит код с корутинами. Да и совершенно непонятно, как это все будет исполняться, ни в одном документе из трех нет подробностей — будет это типичный в основном однопоточный async/await для асинхронности как в C#, т.е. распиленный на теже самые колбэки код, но с видом сбоку, или будет полноценная многозадачность с планировщиком как в Go. Даже первое конечно сильно лучше текущего положения дел, но, имея опыт работы с C# и С++ async/await из UWP, принципиально это ситуацию не улучшает. Мало мальски сложный C# код обрастает кучей костылей и не очевидного поведения. C++ так вообще превращается в страшный ужас даже на простом коде. Так что Go можно спать спокойно, ничего близко по удобству работы с конкурентным кодом не предвидится. Тем более что сам подход принципиально другой с зелеными потоками.
            +1
            А можете раскрыть мысль? Да, корутины будут stackless. Да, это просто хитрое преобразование кода. Но почему это мешает использовать их в многопоточном коде?

            Да даже семантику Go должно быть несложно воспроизвести на основе новых корутин.
              +1
              Потому что async/await всей своей сутью противится многопоточности. По крайней мере по опыту C#, его основное и главное применение это асинхронный однопоточный код с маленькими кусочками, которые улетают в тред пулы для того же сетевого взаимодействия, чтобы потом прилететь обратно на тот же поток после await. Сложно так сходу описать все, но золотые горы, которые рисовал async/await, так и не сбылись, а на выходе получилось решение с кучей подводных камней, неочевидного поведения и ужасов с отладкой. С++ вряд ли сделает что-то лучше, а в силу самого языка будет все еще хуже только. Опять же, лучше, чем ничего, но странно сравнивать это с языками с зелеными потоками, которые являются частью самого языка и его рантайма. По определению не получится достичь того же удобства, да и сам подход другой принципиально.

              Да даже семантику Go должно быть несложно воспроизвести на основе новых корутин.

              Сложно. Go это сложный и умный рантайм, без которого вся эта семантика коту под хвост. С приходом вытесняющей многозадачности рантайм избавится даже от расставленных компилятором точек переключения, которые в некоторых исключительных случаях приводят к блокировкам или голоданию.
                0
                По крайней мере по опыту C#, его основное и главное применение это асинхронный однопоточный код с маленькими кусочками, которые улетают в тред пулы для того же сетевого взаимодействия, чтобы потом прилететь обратно на тот же поток после await.

                Всё зависит от планировщика. Вам ничто не мешает написать свой планировщик, который будет действительно однопоточным и работать с iocp/epoll. Я уже писал такое в качестве эксперимента — разгонялся до миллиона IOPS, так что уверен, что механизм рабочий и жизнеспособный. Единственное — пришлось отказаться от штатных Socket и Delay и заменить их своими реализациями.


                Сложно так сходу описать все, но золотые горы, которые рисовал async/await, так и не сбылись, а на выходе получилось решение с кучей подводных камней, неочевидного поведения и ужасов с отладкой.

                Да, технология имеет низкий порог вхождения, но при этом довольно сложная. Но если вы понимаете все эти сложности, то никаких проблем с её использованием не будет. И я немного жалею, что этой технологии не было лет 10 назад — у меня был проект, где она бы оказалась очень в тему.


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

                Потому что, внезапно, в C++ для этого не нужна поддержка со стороны языка. Эта проблема решается библиотеками. Ну а то, что их не включают в стандартную библиотеку — так это следствие самой философии C++.


                Сложно. Go это сложный и умный рантайм, без которого вся эта семантика коту под хвост.

                Сложно — не значит невозможно.


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

                Так это принципиально разные вещи. Вытесняющая многозадачность — это обычное параллельное выполнение задач на пуле потоков. Как раз тот сценарий, от которого Go пытался избавиться за счёт горутин и планирования в userspace.

                  +1
                  Корутины в C++20 нисколько не противятся многопоточности. Можно посмотреть например на мини туториал, и запустить writerQueue.Run(); из нескольких потоков.

                  Подход корутин в C++20 и правда отличается от Gо. При этом корутины С++20 позволяют полностью избегать динамических аллокаций (или позволяют использовать свои хитрые аллокаторы) и не требуют GC.

                  Подход со stackfull корутинами всё ещё валиден в C++. Например мы в Yandex.Taxi его реализовали и успешно используем. Достигли такого же удобства как и в Go (опять таки без всякого GC). И не мы одни такие, есть пяток опенсорсных движков от крупных компаний, реализующих те же идеи.
                    0
                    А можно названия опенсорсных движков? Очень интересно поглядеть.
                      0
                        0
                        Спасибо. Нашёл ещё github.com/facebook/folly/tree/master/folly/fibers.
                        Я темой корутин интересовался давно — в 2008-м писал eDonkey-клиента на IOCP (правда там не корутины, а callback hell, но вобщем понятно), в 2013 написал свой фреймворк поверх бустовых корутин, включавший собственный корутиновый http-клиент. Потом забросил, а недавно обнаружил boost::fibers. Хочется разобраться во всём современном зоопарке, поэтому и интересно найти максимум библиотек на эту тему.
                +1
                Вам ничто не мешает и прямо сейчас реализовать функционал stackfull со-программ как в Go, для этого не нужна поддержка со стороны языка. Всё, что вам понадобится — это написать собственный планировщик. Это совсем несложно. Скорее, понадобится серьёзная доработка отладчика, чтобы поддерживать отладку пользовательских потоков.

                А вот для чего нужна языковая поддержка, так это stackless генераторы. Вот их и собираются добавить.
                  +1
                  Это совсем несложно

                  Конечно. Почитайте на досуге райнтам Go. Всего-то годы работы первоклассных инженеров, которые до сих пор вылавливают неочевидные кейсы, которые возникают в high-load проектах.
                    +2

                    Написать однопоточный планировщик "зелёных" потоков на C++, который будет уметь работать с таймером и сокетами, и при этом иметь прекрасную производительность (держать десятки тысяч соединений и не тупить) — это действительно несложно, на него нужно потратить пару дней — сужу по своему опыту, т.к. писал его в качестве разминки для мозгов.


                    А вот написать масштабируемый на несколько потоков планировщик, да ещё и определёнными потребностями со стороны HighLoad — это действительно сложно.


                    P.S. Опыт также показывает, что написание велосипеда для решения конкретной задачи оказывается эффективнее, чем использование универсального решения. В HighLoad и не так заморачиваются.

                  +1
                  Насколько я помню, вся «полноценная многозадачность» в Go заканчивается при любом интеропе или системном вызове.

                  Для такого языка как C++ такой вариант просто недопустим.
                –46
                Все программисты на C++ уже вымерли, остался лишь кружок «в американском городе Кона»
                  +14

                  Скажите это Autodesk, Dassault Systemes, PTC, Siemens. Посмеёмся вместе.

                    +17
                    Ещё можно сказать это Yandex, Yandex.Taxi, Google, Facebook, 95% игровых движков, Microsoft, Apple, всем современным автомобильным производителям, Лаборатории Касперского, Bloomberg и остальным HFT компаниям, CERN, IBM, Intel, всем производителям браузеров…
                    +1
                    octoverse.github.com/projects#languages

                    Ну а так-то да, «азаза затралено».
                      +1
                      Вроде у всех на слуху современные C++ проекты:
                      — AI — Фреймворки искусственного интеллекта: Caffe, Caffe2/pytorch, Tensorflow, mxnet, MS CNTK, tiny-dnn
                      — DLT — Распределенные реестры: Bitcoin, Ethereum, Ripple, EOS, Stellar, Monero, Zcash, Nano, Zilliqa, Metahash
                      +26

                      Мне кажется, уже давно настал момент, когда пора выделить "хороший и современный" кусок C++ в отдельный язык (назвать его C++ Lite, например), потому что в противном случае изучить C++ новичку будет в скором времени уже практически невозможно :).

                        0

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

                          +1
                          Это частично исправляют Concepts — с ними можно забыть о страшных std::enable_if и прочем SFINAE.

                          Полный фикс, с человеческой кодогенерацией, подоспеет вместе с Reflections TS.
                          +5

                          Это уже сделано, просто нужно придерживаться C++ Core Guidelines (анонс был еще в сентябре 2015).

                            –1
                            -
                              +1
                              Шаблоны — мощь С++, она же боль.
                              0
                              Блин, хотел переползать на RUST потихоньку, а тут вкусняшки :)
                                +4
                                Мне в комитете сказали, что я ещё стал разработчиком Rust, сам того не зная.

                                Сейчас в Rust думают на корутинами, и их очень сильно смущает выбор C++ со страшными ключевыми словами co_await/co_yield. Моё предложение на нормальные ключевые слова их очень сильно заинтересовало, они ждут развития событий. Как комитет по C++ решит, какие выводы сделает — на основе этого Rust будет решать, как делать у себя.
                                  0

                                  У Rust есть вариант сделать атрибут #[async] вместо полноценного кейворда и, как результат, получить кейворд await! в виде контекстного макроса.


                                  Кстати, почему действительно не использовать атрибуты?


                                  [[async]] future<int> somefunc() { ... }
                                    0
                                    Изначально считалось, что атрибуты не должны сильно влиять на код. Поэтому вместо атрибута можно просто добавить контекстно зависимое слово async, не ломая существующий код и получая тот же результат:
                                    struct y‌ield{};
                                    struct a‌wait{};
                                    struct async{};
                                    
                                    template <class T>
                                    future<int> some_coro() async {
                                        await something;     // Аналог co_await из Coroutines TS
                                        yield something2;    // Аналог  co_yield из Coroutines TS
                                        async something3;    // OK
                                        return something3;   // Аналог  co_return из Coroutines TS
                                    }
                                      +4
                                      Кстати, почему действительно не использовать атрибуты?

                                      потому что All attributes unknown to an implementation are ignored without causing an error (since c++17). В данном случае нужен механизм, гарантирующий определенное поведение
                                        +1

                                        Вот это поворот… Интересно, когда получим "any textual string is a valud program in C++".

                                      +9
                                      их очень сильно заинтересовало, они ждут развития событий. Как комитет по C++ решит, какие выводы сделает — на основе этого Rust будет решать, как делать у себя.

                                      А пацаны-то и не знают.


                                      В Rust уже год можно пользоваться yield в корутинах (они называются генераторами), и полгода назад произошла тихая революция: а именно формально доказали, что Rust способен гарантировать безопасность выполнения корутин с захватом локальных переменных. Т.е. если захватили переменную, то перемещать генератор на стеке нельзя. И компилятор ударит по рукам, если захотите перемещать генераторы. И есть свои механизмы перемещения генераторов, например, через хип и самоссылающиеся структуры.


                                      И async/await давно можно пользоваться, например, для описания комбинаторов. Но почему-то ждут, как же сделают в С++.


                                      Вы там прувы накидайте, как ваше предложение их заинтересовало, а то мне кажется, что вы пустослов.

                                          +2
                                          пересказываю услышанное; не ручаюсь за достоверность.

                                          Не ручаетесь за достоверность? Оукей.

                                          +1
                                          Ну и «Requirements Rust nightly-2019-02-19 for async_await, await_macro» по той ссылке что вы кинули как бы намекает, что вы тоже не сильно в теме готовности этого решения.

                                          Эти ключевые слова уже высечены в камне для Rust, или всё ещё может поменяться?
                                            +2
                                            по той ссылке что вы кинули как бы намекает, что вы тоже не сильно в теме готовности этого решения.

                                            Аккаунт https://github.com/kpp связан с https://habr.com/en/users/humbug/. Та либа лежит в репе https://github.com/kpp/futures-async-combinators, из чего следует, что вы говорите с автором библиотеки. Внимательнее надо быть ;)

                                              +3
                                              Спасибо, впредь буду внимательнее
                                              +4
                                              Эти ключевые слова уже высечены в камне для Rust, или всё ещё может поменяться?

                                              Как ключевые слова они зарезервированы, т.е. невозможно создать переменную с именем async.


                                              Другое дело, что решения Rust с корутинами сильно отличаются безопасностью.


                                              Например, в предложенном варианте N4775 есть такая фраза:


                                              [Note: If a coroutine has aparameter passed by reference, resuming the coroutine after the lifetime of the entity referred toby that parameter has ended is likely to result in undefined behavior.— end note]

                                              Rust тоже столкнулся с таким вопросом, но было предложено безопасное решение (без UB, без проверок в рантайме, полностью zero-cost и без пенальти) в цикле статей:


                                              Async/Await I: Self-Referential Structs
                                              Async/Await II: Narrowing the Scope of the Problem
                                              Async/Await III: Moving Forward with Something Shippable
                                              Async/Await IV: An Even Better Proposal.


                                              И таки теперь это решение в стабильной версии компилятора. И естественно на нем основываются генераторы и async/await. Ну круто же? =)

                                                +6
                                                Да, с безопасностью в Rust очень круто! С этим спорить бесмысленно
                                                  0
                                                  Ну круто же?
                                                  Круто, что быстро запилили (и мы уже активно используем), но вот сам механизм pin/unpin довольно сложный для понимания на мой взгляд, и большинство реализаций «стандартных» комбинаторов требуют unsafe (пример)

                                                  До этого комбинаторы были проще внутри.
                                                    0
                                                    Pin/Unpin все-таки костыль, который позволяет добавить механизм «после активации перемещать низя». Сделать красиво и удобно можно было бы, но пришлось бы пересматривать все основы, то есть менять борроучекер и его правила, на что авторы языка не готовы были пойти.
                                                      0
                                                      Могут попробовать во второй версии языка исправить.

                                                      Вообще самая большая проблема C++, которая была до сих — отсуствие модулей.

                                                      Потому что из-за их отсутствия код «чужой» библиотеки вставлялся прямо в «новый» код через #include. То есть совместимость требовалась полная — вплоть до C++98.

                                                      Вроде rust может себе позволить развести разные «правила игры» в разных модулях, так что в версии 2 могут и исправить правила борроучекера… Не знаю пока C++23 (и последующих) — можно ли там будет такие вещи делать…
                                                    –3
                                                    И таки теперь это решение в стабильной версии компилятора.

                                                    В чём смысл достижения? Это банальность банальная — константный указатель. К тому же, в ситуации с C++ — это вообще ненужно, т.к. ссылка итак pinned. Это какая-то борьба с расто-проблемами не актуальными в С++? Или это действительно такая наивность?

                                                    К тому же, это никак не решает проблему времени жизни — если локальный сторедж для объекта умрёт — объект умрёт. А временем жизни этого стореджа — управлять нельзя. Управлять временем жизни можно только в глобальном сторедже — т.е. в хипе. А это уже оверхед и подобные рассуждения:

                                                    без UB, без проверок в рантайме, полностью zero-cost и без пенальти)

                                                    По умолчанию являются несостоятельными.

                                                    К тому же, все всегда забывают, что «ограничение» — есть оверхед и никакого zero-cost уже нет. Допустим, если мы вводим ограничение на кол-во мутабельных ссылок — это уже оверхед и не zero-cost и любые попытки назвать это zero-cost — враньё и манипуляции.

                                                    К тому же, существует так же синтаксический оверхед, который адептами раста постоянно игнорируется. Если какое-то решение требует от меня во много раз больше синтаксического мусора, нелепой помощи компилятору и прочей борьбы с ограничениями и костылями — это решение не может сравниваться с другим решением без учёта этих нюансов. А это очень важные нюансы.
                                                      +5

                                                      Я тут немного полазил по вашим комментариям. Давайте я раскрою вам глаза на происходящее =)


                                                      Да, настоящее время раст имеет ровно один компилятор — но это не значит, что так будет всегда.
                                                      Какая глупость. (автор этих слов 0xtcnonr)

                                                      Компиляторов уже два. Посмотрите на https://github.com/thepowersgang/mrustc, пару месяцев назад проскальзывала статья, как они забутстраппились.


                                                      К тому же, это никак не решает проблему времени жизни — если локальный сторедж для объекта умрёт — объект умрёт. А временем жизни этого стореджа — управлять нельзя. Управлять временем жизни можно только в глобальном сторедже — т.е. в хипе.

                                                      А вы уверены, что вы правильно поняли семантику Rust? В С++ время жизни — это рантаймовое свойство, тогда как в Rust это статическое свойство, известное на этапе компиляции. Стораджей-ссылок нет.


                                                      К тому же, все всегда забывают, что «ограничение» — есть оверхед

                                                      Да, вы правы. Поэтому когда в C++ замыкание захватывает объект по ссылке, а объект умирает, то ссылка оказывается вникуда. Конечно же это описано в требованиях к правильному использованию С++ замыканий, тем не менее это ментальный оверхэд, за которым надо следить самому.


                                                      Допустим, если мы вводим ограничение на кол-во мутабельных ссылок — это уже оверхед и не zero-cost и любые попытки назвать это zero-cost — враньё и манипуляции.

                                                      И так как время жизни в Rust — это статическое свойство, известное на этапе компиляции, подсчет кол-ва мутабельных/иммутабельных ссылок стоит ровным ничего(прямо как С++ шаблоны), компилятор может сделать вывод о том, будет ли объект, на который ссылается ссылка, жить достаточно, чтобы замыкание/корутина/тред могли использовать ссылку или нет.


                                                      В чём смысл достижения? Это банальность банальная — константный указатель. К тому же, в ситуации с C++ — это вообще ненужно, т.к. ссылка итак pinned. Это какая-то борьба с расто-проблемами не актуальными в С++?

                                                      В Раст существует настоящая мув-семантика, например:


                                                      fn main() {
                                                          let b = String::from("Hello world!");
                                                          let a = b; // Перемещение
                                                          println!("a = {}", a);
                                                      }

                                                      После перемещения обращаться к b переменной нельзя. Она исчезла, переместилась, ее больше нет. Это сделано специально, чтобы избежать неопределенного поведения, присущего С++:


                                                      std::vector<std::string> v;
                                                      std::string str = "example";
                                                      v.push_back(std::move(str)); // str is now valid but unspecified
                                                      str.back(); // undefined behavior if size() == 0: back() has a precondition !empty()

                                                      Итак, у нас есть мув-семантика, следовательно объекты можно действительно перемещать. При перемещении стековых объектов возможно фактическое перемещение объекта на другой адрес, и если в объекте есть поля-ссылки на свои же поля (самоссылающиеся структуры), то перемещение объекта приведет к инвалидации этих ссылок (указывают на старый адрес). Так как в Rust нет GC, то компилятор не может подправлять адреса самоссылающихся структур. Генераторы и корутины являются частным случаем самоссылающихся структур, и для них мув семантика может быть опасна. Поэтому было принято такое решение:


                                                      • Если это самоссылающаяся структура на стеке, то ее нельзя перемещать. Пользоваться такой корутиной можно, но локально. Ее можно создать на стеке, но нельзя возвращать из функции и нельзя перекидывать в другую переменную по типу let a = b. За этими нельзя следит компилятор без пенальти для рантайма, без UB, полностью zero-cost.
                                                      • Но можно создать самоссылающуюся структуру в хипе и, как вы правильно заметили, перемещать константный указатель без перемещения содержимого. Это вариант с пенальти по хипу.

                                                      В Rust реализовали оба, пользуйся любым.


                                                      Почитайте статьи, это действительно увлекательное чтиво.


                                                      Async/Await I: Self-Referential Structs
                                                      Async/Await II: Narrowing the Scope of the Problem
                                                      Async/Await III: Moving Forward with Something Shippable
                                                      Async/Await IV: An Even Better Proposal


                                                      В чём смысл достижения?

                                                      std::pin — это возможность дать компилятору инфу о том, можно ли перемещать объект или нет. Т.е. история с "переместить стековую корутину нельзя, а хиповую можно" было описано чисто языковыми средствами, а не захардкожено в компиляторе.


                                                      Не стесняйтесь обращаться по непонятным раст вопросам в комменты, ко мне в личку, или в гиттер сообщества https://gitter.im/ruRust/general. Тепло встретят, помогут, подскажут, разжуют, объяснят. Без смс и без регистрации =)

                                                        +3
                                                        Компиляторов уже два. Посмотрите на github.com/thepowersgang/mrustc, пару месяцев назад проскальзывала статья, как они забутстраппились.
                                                        Я бы еще добавил парсеры IntelliJ Rust и rust-analyzer, которые по уровню «понимания материала» постепенно приближаются к компиляторам. Разве-что, код генерировать не умеют, хотя последний, на минуточку, метит в сторону замены собой основного фронтенда языка, что как бы намекает.

                                                        С учетом того, что основная сложность Rust приходится именно на семантику языка и его систему типов, а не на кодогенерацию, то такое сравнение я считаю уместным.

                                                        Итого 4.
                                                          –8
                                                          А вот и пошла типичная подмена понятий, адепты выдумывают новые значения понятия «компилятор».

                                                          С учетом того, что основная сложность Rust приходится именно на семантику языка и его систему типов, а не на кодогенерацию, то такое сравнение я считаю уместным.

                                                          Один тезис нелепей другого. У нас есть 4 компилятора, каждый «сложнее» кодогена и оптимизатора, т.е. llvm«а, но почему-то без llvm»а никуда? Как же так? Неужели опять путаница в показаниях? Какая досада.

                                                          Но я сообщу адептам важную новость — компилятор, это то, что производит из исходного кода программу, причём программу релевантную.

                                                          К тому же, мы тут видим очередную попытку врать и игнорировать контекст от адептов. О чём говорилось там? О кол-ве таргетов и о том, что у раста нет компилятора на расте и он зависит от С/С++ и в компилтайме и в рантайме.

                                                          Что же нам предлагают адепты?

                                                          IntelliJ Rust и rust-analyzer,

                                                          Не компиляторы. Причём первый неизвестно на чём написан, т.е. уже проблема.

                                                          mrustc, rustc

                                                          Первый вообще целиком и полностью на С++ и имеет его© в качестве таргета, второй генерирует llvm-ir при помощи llvm-рантайма.

                                                          И того, кол-во реальных таргетов равно нулю. Количество компиляторов — равно нулю. Есть два транслятора — rust -> ir, rust -> С, к тому же второй не является полноценным(т.з. базовой методички, ведь (i.e. without borrow checking)).

                                                          Итого 4.

                                                          Главное верить, врать и плюсовать друг друга. В этом сила.

                                                            +4
                                                            С подобным отношением к собеседнику пройдите-ка, пожалуй, на ЛОР. У меня нет ни малейшего желания дискутировать.
                                                              –8
                                                              Ответить нечего — так и запишем. Вас поймали на вранье и как всегда, вместо ответов я увидел оправдания. К тому же, это не я побежал себя минусовать без малейших объяснений. А если кто-то бежит минусовать меня и минусовать какого-то балабола в интернете, который наврал — это адепты, это сектанты и я имею полное право их так называть.

                                                              Поэтому, вам просто не повезло ответить мне и результат чего был ожидаем.
                                                              +2
                                                              А вот и пошла типичная подмена понятий, адепты выдумывают новые значения понятия «компилятор».

                                                              Я человек простой, clang — compiler front end для C++, а rustc — compiler front end для Rust. Не?

                                                                –4
                                                                Я человек простой, clang — compiler front end для C++, а rustc — compiler front end для Rust. Не?

                                                                Не, вопрос был в том, что «раст более портируемый» и как мы выяснили «фронтенд сложнее милд/бек-ендов». И то и то опровергается.

                                                                А llvm часть инфраструктуры крестов, создавался для шланг и под шланг. Шланг настолько же портируемый как кресты/си, т.к. зависим только от них. Шланг написан на крестах, а значит является полноценный компилятором на крестах.

                                                                К тому же, куда подевался тот же gcc? А это топ1 компилятор.
                                                            –5
                                                            Компиляторов уже два. Посмотрите на github.com/thepowersgang/mrustc, пару месяцев назад проскальзывала статья, как они забутстраппились.

                                                            Об этом я написал выше.

                                                            А вы уверены, что вы правильно поняли семантику Rust? В С++ время жизни — это рантаймовое свойство,

                                                            Это всегда рантаймовое свойство. Это базовая семантика стека и чего бы вы там не придумали — оно этого факта не изменит.

                                                            тогда как в Rust это статическое свойство, известное на этапе компиляции. Стораджей-ссылок нет.

                                                            Это не более чем манипуляция. Можно что угодно называть чем угодно, но это ничего не изменит. «Стораджей-ссылок» есть всегда.

                                                            Да, вы правы. Поэтому когда в C++ замыкание захватывает объект по ссылке, а объект умирает, то ссылка оказывается вникуда. Конечно же это описано в требованиях к правильному использованию С++ замыканий, тем не менее это ментальный оверхэд, за которым надо следить самому.

                                                            Какие-то новые откровения. Почему мы все другие оверхеды игнорируем, а тут вдруг сразу «оверхед — и плохо»? К тому же, других решение нету. Но об этом позже.

                                                            И так как время жизни в Rust — это статическое свойство, известное на этапе компиляции, подсчет кол-ва мутабельных/иммутабельных ссылок стоит ровным ничего(прямо как С++ шаблоны)

                                                            Опять какие-то мантры и ни на чём не основанные утверждения. Можно мне пример с парой(активных) мутабельных ссылок? Можно мне пример подсчёта времени жизни без синтаксического мусора(с передачей объектов в функции и т.д.)?

                                                            компилятор может сделать вывод о том, будет ли объект, на который ссылается ссылка, жить достаточно

                                                            Он будет всегда жить недостаточно — это базовое ограничение стека. Его ничем и никак не обойти. Только оверхедом, но в этом случае вы запутались в показаниях.
                                                            чтобы замыкание/корутина/тред могли использовать ссылку или нет.

                                                            Они никогда не могут использовать ссылку, т.е. любой не-глобальный объект имеет непредсказуемое с т.з. замыкание/корутина/тред время жизни.

                                                            В Раст существует настоящая мув-семантика, например:

                                                            Любой адепт называет свою веру «настоящей», только это ничего не меняет. Где критерии «настоящий» и где обоснование этих критериев за «единственно верные»? Нету, ну это достаточно типичная ситуация.

                                                            Это сделано специально, чтобы избежать неопределенного поведения, присущего С++:

                                                            О боже, эти нелепые манипуляции. Когда в одном случае показывают одно, а в другом другое. Где идентичный код на С++? Нету, потому что «не получилось» и пришлось выдумать новый пример?

                                                            std::vector<std::string> v;
                                                            std::string str = "example";
                                                            v.push_back(std::move(str)); // str is now valid but unspecified
                                                            str.back(); // undefined behavior if size() == 0: back() has a precondition !empty()

                                                            Ещё более нелепые манипуляции. На кого это рассчитано? Какое отношение эти попытки имеют к move? Да никакого.

                                                              std::string str;
                                                              str.back(); // undefined behavior if size() == 0: back() has a precondition !empty()<

                                                            Опять попытки обмануть выдавая дефолтное поведение строки за следствие move? Для протокола поясню — метод back() не возвращает никаких ошибок, поэтому он не может сообщить об ошибке. Так сделаано для потому, чтобы не городить синтаксический мусор из которого состоит обработка ошибок на расте.

                                                            Итак, у нас есть мув-семантика, следовательно объекты можно действительно перемещать.

                                                            Ну манёвры я уже понял — поясню читателям. Есть данные — они хранятся в неком «локальном сторедже»(т.е. время жизни привязано к потоку выполнения). Эти данные перемещать никуда нельзя — только копировать.

                                                            Так что же такое перемещение? В объекте могут быть данные хранящиеся в глобальном сторедже(т.е. там, где их время жизни ничем не ограничено) — это некие ресурсы. В том же raii время жизни этих ресурсов привязывает к времени жизни объекта — это владелец ресурса. В базовом виде владелец всегда один и move — это и есть передача владения.

                                                            Мало того, что это уже оверхед и не zero-cost, т.к. привязка одного к другому может быть менее эффективно, нежели отсутствие каких-либо привязок, а следовательно отсутствие вообще какой-либо проблемы «времени жизни».

                                                            Два основных тезисы — все объекты из локального стореджа копируются ВСЕГДА. Все проблемы связанные с временем жизни — вызваны raii.

                                                            При перемещении стековых объектов возможно фактическое перемещение объекта на другой адрес

                                                            Оверхед. Ссылка предполагает передачу без копирования. move — копирование. Никаких «фактическое», «перемещение» и прочих нелепых базвордов — не существует. Это всё про ресурсы, а не объекты в локальном сторедже(стеке).

                                                            и если в объекте есть поля-ссылки на свои же поля (самоссылающиеся структуры), то перемещение объекта приведет к инвалидации этих ссылок (указывают на старый адрес).

                                                            Опять же, подмена понятий. Не перемещение, а копирование. Перемещение — это про глобальные ресурсы, у которых адреса никак не изменяются.

                                                            Кстати, тут можно заметить — как человек игнорирует некий основной момент. Дело в том, что "(самоссылающиеся структуры)" — это не единственный кейс, в котором происходит «инвалидации этих ссылок». Любая ссылка на объект — это уже та самая "(самоссылающиеся структуры)" и любое «перемещение» объекта — вызовет инвалидацию всех ссылок на этот объект.

                                                            Именно поэтому там выше выдаётся убогость за плюс:

                                                            После перемещения обращаться к b переменной нельзя.

                                                            Из этого следует, что b уже ссылка, а не сторедж. В любом случае — это инвалидирует все ссылки на b. Ах да, как я могу забыть — мы же можешь жить в мире поней, где «ссылки иметь нельзя» и в этом мире всё нормально. Только это не более, чем убогие абстракции, которые никоим образом не являются zero-cost.

                                                            Если это самоссылающаяся структура на стеке, то ее нельзя перемещать.

                                                            Копировать — опять манипуляции. Копирование — уже оверхед. Ссылочная передача не вызывает копирования и не приводит к оверхеду. На этом уже можно закончить, вы уже запутались в показаниях.

                                                            Пользоваться такой корутиной можно, но локально.

                                                            Это не имеет смысла. В этом случае всё можно передавать по ссылкам, не получая никакого оверхеда.

                                                            Ее можно создать на стеке, но нельзя возвращать из функции и нельзя перекидывать в другую переменную по типу let a = b.


                                                            За этими нельзя следит компилятор без пенальти для рантайма, без UB, полностью zero-cost.

                                                            Опять какие-то нелепые манипуляции. Это типичная тактика — сражаться с соломенным чучелом. Кто и где говорил, что отслеживание подобного вообще кому-то интересно? Я об этом говорил? Нет. Сам придумал — сам опроверг, как говориться.

                                                            К тому же, оверхед здесь есть и я об этом сообщил выше — «копирование», которое тут манипулятивно называется каким-то «перемещением».

                                                            Хотя ладно, я разоблачу и эту историю. Дело в том, что в подобных попытках все рассуждения существуют в рамках подложного контекста. Допустим, в данном случае предполагается, что поток управления — основание, под которым ничего нет.

                                                            В реальности же, я могу сделать что угодно — хоть привязать время жизни стека к времени жизни корутины, хоть скопировать контекст, хоть остановить поток на уровне ОС. Очевидно, что ничего из этого подобные схемы не учитывают, но адептами подобных изваяний выдаются за «аналоги», либо за полные(т.е. те, которые учитывают всё).


                                                            В Rust реализовали оба, пользуйся любым.

                                                            Опять же, тут видно явные подлоги. Адепт выдумывает какие-то два случая, которые существуют в рамках его фентейзиного мира, но при этом выдаёт их за реально — т.е. будто был ТОЛЬКО эти два случая существуют. На самом деле нет, и об этом я уже говорил выше.

                                                            Дак мало того, что два случая — вранье, дак ещё и каждый случай — враньё. На самом деле тут прослеживается типичная тактика, когда адепт какого-то учения говорит тезисами понятиями этого учения, которые непротиворечивы в рамках этого учения(этого понятийного аппарата). Но проблема в том, что мне как-то вообще не интересно это учение и эти понятия никак не соотносятся с реальным миром, и их «внутренняя корректность» никак и ничего не означает.

                                                            Т.е. это обычный такой фентезийный мир, где существует какая-то физическая модель и всё повествование ей соответствует. А далее приходит подмена контекста, мы подменяем мир фентезийный на мир реальный, в котором действуют совершенно другие законы и существуют совершенно другие понятия. И вся аргументация адептов этого мира сводится к жонглированию этими самыми фентейзиными понятиями и упование на их корректность. Но они не работают за рамками контекста(фентезийного) и за рамками контекста(фентезийного) адепт ничего не знает, именно поэтому он всегда пытается затащить оппонента в этот самый контекст. Это всегда очень явно прослеживается в спорах с подобными людьми. Это видео и тут.

                                                            Почитайте статьи, это действительно увлекательное чтиво.

                                                            Банальная попытка накрутить значимости очередной нелепости, делая вид «какой же оппонент тупой — не читал такого чтива».

                                                            std::pin — это возможность дать компилятору инфу о том, можно ли перемещать объект или нет. Т.е. история с «переместить стековую корутину нельзя, а хиповую можно» было описано чисто языковыми средствами, а не захардкожено в компиляторе.

                                                            Тут мы видим опять какие-то глупые попытки манипулировать, выдавая убогость за плюс. Но я расшифрую это.

                                                            Мы просто запретили всё, но срочно понадобилось решение проблемы. Поэтому наделали unsafe-хаков, и везде раструбили о том, что «мы нашли решение» — на самом деле нет. Это значит только одно — начальная модель несостоятельна, раз требуется её взламывать. Но вера есть вера.

                                                            На самом деле это фундаментальная манипуляция на котором основан раст. Раст разделён на два языка — язык на котором НИЧЕГО НЕЛЬЗЯ НАПИСАТЬ(базовый), и язык на котором НАПИСАТЬ ЧТО_ТО МОЖНО(unsafe). Как дальше действуют адепты, т.к. на базовом языке ничего написать нельзя, то они пишут на unsafe-языке базовые примитивы, а далее выдают их корректность за корректность языка(на самом деле тут такой же подлог. Корректность модели — не означает корректность имплементации). Далее, путём компоновки этих примитивов уже пишется код на базовом языке.

                                                            По-сути они пытаются впарить корректность библиотеки за корректность языка, сравнивая это с тем же С++. В С++ любые библиотеки так же корректны, но в чём заключается подлог? А подлог заключается в том, что язык не разделён на два, хотя раст тоже не разделён на два, но его адепты всегда поют мантру «unsafe всегда видно» и это единственное отличие их языка от любого другого.

                                                            А далее происходит ещё более нелепая схема. «ты в крестах можешь передать по ссылке и сломать, а у нас не можешь», только вот адепты тебе не скажут, что «не можешь» относится не к «сломать», а к «передать».

                                                            Их модель не работает с исключениями? Зачем исключения — мы придумаем очередную мантру и будем в неё верить. Мы придумаем свои трактовки zero-cost, будем всегда врать и подменять понятия.

                                                            Тепло встретят, помогут, подскажут, разжуют, объяснят. Без смс и без регистрации =)

                                                            Это как? Будут минусовать неугодынх и плюсовать своих, которые засирают те темы, которые к ним никакого отношения не имеют? Плюсовать просто пофакту того, что «мы одной веры»? О да, всегда мечтал о таком.

                                                              0

                                                              А что для вас zero-cost?

                                                                –1
                                                                То, что следует из этого понятия. Это значит, что написать «в рамках абстракции» я могу так же эффективно, как «в обход абстракции». Любые ограничения, которые забирают у кода эффективность — не являются zero-cost. Даже крестовое raii не является zero-cost. И для поддержания хотя-бы минимальной эффективности нужно постоянно латать абстракции.

                                                                Очень часто что раст-адепты, что крестовые адепты определяют zero-cost как «если мы напишем такой же код, который обеспечивает нашу абстракцию, то он не будет быстрее».

                                                                Пример я уже приводил выше. Допустим, у нас есть объекты, которые создаются и удаляются. Абстракции работает с памятью как с неким ресурсов, уникальным для каждой каждой аллокации, а значит должна для каждой аллокации делать обратную операцию.

                                                                Под абстракциями всё не так — я могу снести хоть весь хип. И никакой обратной операции вызывать не нужно. Это уже оверхед.

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

                                                                Да, эти хаки можно(в каких-то случаях) сделать и на уровне имплементации самой абстракции, но это всё происходит тогда, когда кто-то обжёгся и кто-то придумал и рассчитывать на это нельзя. К тому же — это отнимает предсказуемость и лишает возможности вообще понимать «как работает».

                                                                Подобными хаками раст-адепты очень часто обманывают и пускают пыль в глаза зелёным сишно-крестовым адептам, которых, на самом деле, очень просто обмануть и запутать.

                                                                Но факт остаётся фактом — ты не можешь рассчитывать на это(как минимум тебе нужно знать все существующие оптимизации в stdlib, что уже почти невозможно сделать), это работает в самых примитивных с случаях и работает очень плохо.

                                                                Поэтому, как только кто-то пытается повторить на расте(и подобных «безопасных» изваяниях) реальный zero-cost, когда какой-то адепт будет перепащивать очередной сишный/крестовый код — он будет заниматься сплошным взломом абстракций, либо на уровне stdlib, либо на уровне своего кода.
                                                                  +1
                                                                  Очень часто что раст-адепты, что крестовые адепты определяют zero-cost как «если мы напишем такой же код, который обеспечивает нашу абстракцию, то он не будет быстрее».

                                                                  Ну так трекать время жизни переменных для рантайма раста буквально zero-cost. Это ничего не стоит, так как это считается в момент компиляции и только для компиляции. Что с этим не так?

                                                                    0
                                                                    Ну так трекать время жизни переменных для рантайма раста буквально zero-cost.

                                                                    Это неверное утверждение. Как я уже говорил — эти конструкции и тезисы валидны ТОЛЬКО в рамках внутренней логики раста, которая сводится к «запрещаем множественные ссылки — нет проблем», «запрещаем отделять скоуп от потока выполнения — нет проблем».

                                                                    Т.е., если попроще — этот тезис существует в рамках модели, которая накладывает множество ограничений со стороны возможностей. Это ограничения НЕ ПОЗВОЛЯЮТ РЕАЛИЗОВАТЬ что-то НАСТОЛЬКО ЖЕ ЭФФЕКТИВНО как в случае, КОГДА ЭТИХ ОГРАНИЧЕНИЙ НЕТ.

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

                                                                    Это вообще типичная методика манипулирования. Когда мы выделяем какую-то подзадачу и говорим «ну дак время её выполнения не изменилось», постоянно всем этим тыча. Но, это никого не волнует, ведь нас не интересует время выполнения под-задачи — нас интересует время выполнения задачи. А ваше решение задачи усложняет(т.е. замедляет) все остальные(некоторые другие) подзадачи.

                                                                    Таким образом, в рамках одно части задачи разницы нет, а в рамках всей задачи — разница есть.

                                                                    Что с этим не так?

                                                                    Ну ведь я уже десять раз объяснял. Но ваши ответы, скорее всего, связаны не с «непониманием разницы», которую(разницу) я декларирую, а с идеологическими проблемами.

                                                                    Т.е. у вас есть тезисы, которыми вы пытаетесь крыть мои. Все эти тезисы работают только в заранее ограниченном фентезийном мире, т.к. мы убираем всё неудобное и оставляем удобное. А далее нам нужно просто попытаться завести оппонента в этот фентезийный мир, чтобы он начал обсуждать какой-то нелепые и никому не интересные трекинг, вместо реальных проблем и задач. Не нужно делать так.
                                                                      +1
                                                                      «запрещаем отделять скоуп от потока выполнения — нет проблем»

                                                                      Но ведь Rust позволяет перекидывать замыкания между разными потоками, например создать в одном, передать в какую-нибудь машинерию с шедулером, которая позволит выполнить замыкание на свободном треде.


                                                                      Rust заставляет программиста придерживаться тех же правил, что и C++: не оставлять висячие ссылки, не вызывать рейс кондишны, не нарушать алиасинг… Он не делает это самостоятельно, он выполняет проверки и говорит:


                                                                      • вот тут у тебя объект умрет и вот те ссылки будут ссылаться на мусор и будет UB. почини
                                                                      • вот тут ты хочешь писать в шаренную переменную из другого треда, что приведет к UB. не делай так
                                                                      • вот тут ты пытаешься взять две мутабельные ссылки на один и тот же объект и сделать memcpy одного в другой. атата!

                                                                      Но вы это почему-то называете оверхедом. Почему?

                                                                    +2
                                                                    Любые ограничения, которые забирают у кода эффективность — не являются zero-cost. Даже крестовое raii не является zero-cost. И для поддержания хотя-бы минимальной эффективности нужно постоянно латать абстракции.

                                                                    Верно. Но подсчет ссылок идет во время компиляции и не занимает ресурсов во время выполнения и не забирает у кода эффективности.


                                                                    Вы следуете пути GIGO, так как не разбираетесь в матчасти.

                                                                +1
                                                                После перемещения обращаться к b переменной нельзя. Она исчезла, переместилась, ее больше нет. Это сделано специально, чтобы избежать неопределенного поведения, присущего С++:

                                                                Прошу прощения, но здесь вы все-таки немножко передергиваете. В приведенном вами примере неопределенное поведение связано все-таки не с реализацией move-семантики в C++ («настоящая/не настоящая»), а с реализацией std::string. Точно это же самое неопределенное поведение можно вызвать и без всякой move-семантики, например:

                                                                std::string str;
                                                                str.back();
                                                                

                                                                C++ Core Guidelines рекомендуют оставлять moved-from объект в состоянии «default value of the type», что довольно разумно.
                                                                  0
                                                                  В приведенном вами примере неопределенное поведение связано все-таки не с реализацией move-семантики в C++ («настоящая/не настоящая»), а с реализацией std::string.

                                                                  Я считаю, что это связано именно с реализацией move-семантики. C++ не может позволить себе трекать ссылки на объект во время компиляции (время жизни — рантаймовое свойство), следовательно нельзя фактически перемещать объекты, ибо ссылки будут указывать на невалидную область памяти. Поэтому предполагается оставлять moved-from объект в состоянии «default value of the type», чтобы ссылки хоть на что-то ссылались. И возникновение UB из-за реализация std::string — это все уже следствие семантики.

                                                                    +3
                                                                    Трекать можно, разговоры о разрушающем move в комитете идут достаточно давно. Другое дело, что есть множество проблем, в том числе — добавляя разрушающий move мы получаем новую разновидность move семантики, которая должна сосуществовать со старой и взаимодействовать с ней.

                                                                    Решение ищется, но в ближайшее время вряд ли появится.
                                                                      0

                                                                      Спасибо=)


                                                                      А почему сразу не добавили разрушающий мув?

                                                                        0
                                                                        У меня есть такое понимание, что изначально задача стояла более узкая — оптимизировать работу с rvalue, где все и так разрушабельно практически сразу.
                                                                          +1
                                                                          В историю вопроса не вдавался, есть только догадка: на тот момент казалось что возможность «вымувливать» ресурсы из переменной переданной по lvalue даст больше профита чем уничтожающий move. Так неуничтожающий move проще интегрировать с legacy code: если есть неподконтрольная вам функция foo(T& value, F а), вызывающая f(value), то можно сделать f принимающую параметр по lvalue ref, а внутри уже сделать std::move.
                                                                            +1
                                                                            То есть, сделали так из-за legacy, а теперь и само это решение переходит в legacy, с которым нужно считаться! В Rust, кстати, предвидя подобные проблемы своевременно внедрили механизм редакций.
                                                                              +2
                                                                              Имеющийся механизм хорош для своих целей. Деструктивные перемещения хороши для других целей. Механизмы друг друга не заменяют, legacy не маячит на горизонте.
                                                            +1
                                                            Так оно же еще не стабилизировано, то бишь только в nightly под feature, поэтому и смотрят как это использовать красиво и удобно в том числе и в плюсах.
                                                          +3

                                                          Увы, все вкусняшки мира не спасут от тонн легаси. Так что всё равно попробуйте.

                                                          +2
                                                          А можно ли будет использовать std::flat_* структуры с, например, std::array, если мы точно знаем макс. количество элементов и не хотим динамической аллокации?
                                                            +1
                                                            У std::array нет методов insert, но вот с static_vactor использовать можно будет.
                                                            +4
                                                            Добавьте в опрос пункт «Ничего не пригодится», а то он какой-то однобокий получается
                                                              +7
                                                              У меня лично была проблема выбрать что-то одно, а не всё сразу :)
                                                              +3
                                                              есть подозрение, что больше всего пользоваться я буду таки format'ом, а уже потом всем остальным
                                                                0
                                                                format сделали прям шикарным. Лучше чем в fmtlib, за счёт того что отвязали функции форматирования от медленного std::locale.
                                                                  +2

                                                                  std::locale в частности и iostreams вообще надо бы по хорошему закопать нафиг и больше не трогать.

                                                                    0
                                                                    Прежде чем закапывать, надо предоставить адекватную замену… а иначе придётся откапывать обратно.
                                                                      0

                                                                      Не могу не согласиться. И это вдвойне грустно.

                                                                    0
                                                                    А в стандарт попадет что-то типа print и println с нормальным форматированием?
                                                                      +2
                                                                      std::format + любой метод вывода покрывают функционал print и println:
                                                                      std::cout << std::format("Hello {} {} times\n", "world", 42);
                                                                        0
                                                                        Но согласитесь, это как-то не очень =/
                                                                        Я думаю многим эта фича будет нужна именно для вывода на экран. Ну что ж… будем наблюдать шаблонную my_print в каждом втором проекте =)
                                                                          0
                                                                          Я думал что вся fmtlib перекочует в стандарт. Жаль. Удобная библиотека.
                                                                            0

                                                                            Т.е. он не будет уметь zero allocation из коробки?

                                                                              0
                                                                              std::format умеет форматировать в подставленный пользователем буффер и в этом случае не делает динамических аллокаций (разве что вы попросите его форматировать с использованием std::locale).
                                                                      0
                                                                      Возможность эффективно извлекать строки из std::*stringstream и передавать во владение пользовательские строки P0408

                                                                      Наконец-то! Впрочем, с появлением std::format этот архаизм можно выкинуть на помойку — что ещё лучше.

                                                                        –8

                                                                        А это нормально теперь на Хабре вместо полезного названия использовать кликбейт?.. Что дальше будет? "С С++20 ОНИ СДЕЛАЛИ ЭТО..."?

                                                                          +1
                                                                          Заменил «Приняли ли Modules b Coroutines» на «Coroutines, Modules и прочее». Надеюсь так стало лучше.
                                                                          +2

                                                                          Где-то можно почитать о том, как предполагается использовать модули, со всеми их партишенами и другой эзотерикой? Неясно даже, единицу чего представляет модуль.

                                                                            0
                                                                            На эту тему готовится отдельный документ от комитета «C++ Ecosystem Technical Report». Об этом есть немного вот тут, и ответы 3 на самых частых вопроса вот тут.
                                                                            +3
                                                                            Лично меня радует, что из четырех мажорных фич две пришли из мира ФП и/или из Haskell.

                                                                            Concepts
                                                                            Coroutines
                                                                            Modules
                                                                            Ranges


                                                                            Предлагаю аббревиатуру:
                                                                            CoCoMoRa
                                                                              +1
                                                                              Там ещё и монадические интерфейсы к std::optional обсуждаются, чтобы порадовать любителей ФП.
                                                                                0
                                                                                Их же через корутины легко выразить (а вот для Either у меня не получилось).
                                                                                  0
                                                                                  Да, но хочется готовое изкоробочное решение
                                                                                  0

                                                                                  Мне кажется, важной фичей для std::optional должны быть как раз таки pattern matching, потому что для optional::operator* сказано:


                                                                                  This operator does not check whether the optional contains a value! You can do so manually by using has_value() or simply operator bool(). Alternatively, if checked access is needed, value() or value_or() may be used.

                                                                                  То есть при его использовании возможно возникновение неопределенного поведения.


                                                                                  Конечно можно возразить, что надо использовать вот так:


                                                                                  if (opt.has_value()) {
                                                                                      auto... value = *opt;
                                                                                  }

                                                                                  И что есть optional::value, которое проверяет наличие и возбуждает исключение при отсутствии значения. Но ЁКЛМН, люди ошибаются, опечатываются, неужели вам нужна экономия на спичках, которая приводит к UB? Что быстрее напечатать, *opt или opt.value()? Что будет сильнее бросаться в глаза, *opt или opt.value_unsafe_unchecked(), если уж экономите один бранч на проверке наличия значения?


                                                                                  И паттерн матчинг позволил бы написать один единственно верный метод, который позволил бы и достать и сразу же проверить значение на корректность.

                                                                                    0
                                                                                    Я не знаю наверняка, но выглядит так, будто это сделано чтобы воссоздать поведение boost::optional.
                                                                                    Люди уже привыкли таким образом использовать optional в бусте и не могут проект одним махом перевести с boost::optional на std::optional. Разный принцип работы operator* и ::value() внутри одного большого проекта создаст гораздо больше проблем.
                                                                                  –4
                                                                                  Какие именно?

                                                                                  Concepts — давно уже существовали в документации к библиотекам.
                                                                                  Coroutines пришли из C#
                                                                                  Modules — давно уже есть во всех нормальных языках программирования кроме Си и С++
                                                                                  Ranges — слишком очевидны чтобы приходить откуда-то…
                                                                                  +6

                                                                                  Скажите пожалуйста, а войдет ли в С++20 P0631 (Math constants)? Дождемся ли мы наконец-то числа Пи в стандарте? Или так и будем atan(1)*4?

                                                                                    +3
                                                                                    Или так и будем atan(1)*4?

                                                                                    лучше acos(-1) )
                                                                                      +3
                                                                                      Т_Т
                                                                                      +1

                                                                                      Можете пользоваться <cmath>, там есть M_PI,
                                                                                      upd: а, блин, это же нестандартное расширение GNU Libc

                                                                                        0
                                                                                        Стандарт не требует наличия M_PI, и я вынужден пользоваться компилятором, в котором такой константы нет.
                                                                                          0
                                                                                          в стандарте нет M_PI
                                                                                          0
                                                                                          Их дизайн утверждён для принятия в C++20, но у LWG много вопросов по этому предложению. Так что может и не успеют попасть в C++20.
                                                                                            +11
                                                                                            Спасибо. Буду держать кулаки -_-'

                                                                                            Я до сих пор не понимаю, как в С++11 умудрились пропихнуть столько нетривиальной математики, вроде вихрей Мерсенна, эрмитовых сплайнов и распределений Коши, но при этом не заюзать константы вроде Пи и е.
                                                                                              +3

                                                                                              Не могут решить, чему равно PI в военное время?

                                                                                                +1
                                                                                                Непонятно, должны эти константы находиться в namespace std::math, или же это просто цифры, не обязательно относящиеся к математике. Непонятно, писать их шаблонными переменными типа X или шаблонными переменными типа X const& (в этом случае для редких кейсов получается экономия по месту). Непонятно, как разрешить пользователям кастомизировать эти переменные для своих типов, если до этого кастомизация шаблонных переменных не особенно была разрешена (можно ли добавлять свои переменные в namespace std?). Ну и возникли вопросы про денормализованные числа с плавающей запятой и их использование для подобных констант.
                                                                                                  +3
                                                                                                  Непонятно, должны эти константы находиться в namespace std::math, или же это просто цифры, не обязательно относящиеся к математике.

                                                                                                  Это как раз логично: эти константы должны находиться вместе с функциями, которые с этими константами работают (sin, exp), то есть в std, а не std::math.


                                                                                                  Непонятно, писать их шаблонными переменными типа X или шаблонными переменными типа X const& (в этом случае для редких кейсов получается экономия по месту)

                                                                                                  constexpr, не?


                                                                                                  Непонятно, как разрешить пользователям кастомизировать эти переменные для своих типов, если до этого кастомизация шаблонных переменных не особенно была разрешена

                                                                                                  А зачем эти константы вообще шаблонизировать?

                                                                                                    0
                                                                                                    то есть в std, а не std::math

                                                                                                    Сейчас они в std::math

                                                                                                    constexpr, не?

                                                                                                    Это не панацея, например вот тут https://godbolt.org/z/Le22qh без использования ссылок появляются в коде дублирующиеся данные.

                                                                                                    А зачем эти константы вообще шаблонизировать?

                                                                                                    У пользователей могут быть (и есть!) свои числовые типы. Пользователи языка пишут свои шаблонные числовые алгоритмы:
                                                                                                    template <class Num>
                                                                                                    auto perim(Num r) {
                                                                                                        return 2 * std::math::pi<Num> * r;
                                                                                                    }
                                                                                                    

                                                                                                    Без кастомизации пользователю придётся писать этот алгоритм ещё раз, для типов чисел для которых не специализирован std::math::pi. Если у вас сотня таких алгоритмов, использующих pi — ваша жизнь превратится в боль, а разработка в копипасту.
                                                                                                      +3
                                                                                                      Сейчас они в std::math

                                                                                                      Ну я вообще приверженец #include <math.h>, там всё в глобальном пространстве, просто вот тут написано, что в std, а не std::math.


                                                                                                      Это не панацея, например вот тут https://godbolt.org/z/Le22qh без использования ссылок появляются в коде дублирующиеся данные.

                                                                                                      Да, на существующих архитектурах нельзя загружать float/double как immediate value. Поэтому эта константа должна лежать где-то в памяти. Но константа pi абсолютно ничем не отличается от той же константы 1.0. Мы спокойно пишем в коде 1.0, не задумываясь, куда её положит компилятор. Если же вдруг нужно указать явно ссылку (например, при использовании SSE интринсиков), то тут не сложно положить константу куда-нибудь, где нам удобно, либо написать функцию-обёртку, отдав всё на откуп компилятору.


                                                                                                      Поэтому дайте программисту просто значение, а он уже сам решит, что с ним делать. Не нужно за программиста решать, что это должна быть константная ссылка. Он сам её сделает такой, если захочет.


                                                                                                      У пользователей могут быть (и есть!) свои числовые типы. Пользователи языка пишут свои шаблонные числовые алгоритмы:

                                                                                                      Понимаю эту проблему, но в 99.99% случаев всё покрывается стандартными типами float, double и long double из стандарта IEEE-754. Достаточно ограничиться константами для этих трёх фундаментальных типов.


                                                                                                      Если же пользовать хочет использовать что-то своё, то флаг ему в руки, пусть пишет свой шаблонизированный вариант этой константы.

                                                                                                        0
                                                                                                        Поэтому дайте программисту просто значение, а он уже сам решит, что с ним делать. Не нужно за программиста решать, что это должна быть константная ссылка. Он сам её сделает такой, если захочет.

                                                                                                        Вот как раз шаблонные переменные и спасут вас в этом случае.

                                                                                                          +2
                                                                                                          Вот как раз шаблонные переменные и спасут вас в этом случае.

                                                                                                          Это очень плохое решение. Да, вы можете точно сказать, какое должно быть представление числа пи в стандартных типах данных (float, double, long double). Но какое должно быть представление числа пи в пользовательском типе T, может сказать только программист, но не разработчик стандартной библиотеки.


                                                                                                          template<class T>
                                                                                                          constexpr T pi = T(3.1415926535897932385L);

                                                                                                          И что будем делать, если T — это 128-битное вещественное?

                                                                                            0
                                                                                            constexpr контейнеры и constexpr динамический полиморфизм

                                                                                            Их уже приняли (std::string и std::vector)?
                                                                                              0
                                                                                              Они одобрены для включения в C++20, но еще не прошли подгруппы Core и Library.
                                                                                            +1
                                                                                            Как раз на выходных занимался свои проектом и допиливал логгер на базе fmtlib и потихоньку начал прикручивать тот самый календарь (date) под MSVC из репозитория автора.
                                                                                            Так вот, у меня созрел вопрос: как обстоят дела с часовыми поясами? Я не до конца разобрался в этом вопросе, но меня уже волнует вопрос быстродействия. Поскольку там парсится база часовых поясов и прям вшивается в runtime-api. Далее, насколько я понимаю, current_time_zone не инициализируется при запуске, как делается в либах C, а постоянно запрашивается и через строчный идентификатор возвращает объект date::time_zone. Просто как-бы если вовремя работы поменять часовой пояс, то сишный localtime вернет время по старому часовом поясу…
                                                                                            У кого-нибудь есть не столь поверхностные познания об этих вещах? И проверял ли кто-нибудь быстродействие std::chrono + date в этом вопросе?

                                                                                            P.S. Поюзал денек этот календарь и чет он не особо удобным показался, хотя я был рад его анонсу. А fmtlib в std очень рад. Теперь жду концепты и полноценную поддержку C++20.
                                                                                            P.S.S. constexpr math — вот теперь заживем...)
                                                                                              0
                                                                                              Также в SG1 Concurrency обсуждали идею concurrent unordered map P0652R2.


                                                                                              А чего контейнеры попроще concurrent (queue, stack) или посложнее concurrent ordered map (из libcds) не предложили добавить?
                                                                                                0
                                                                                                Попроще контейнеры начали делать ещё до нас P0260.
                                                                                                До контейнеров посложнее руки ещё не дошли.
                                                                                                  0
                                                                                                  А P0260 будет в C++20?
                                                                                                    0
                                                                                                    Нет :(
                                                                                                      0
                                                                                                      Интересно даже в чем причина — не было достаточного количества спецов по concurrency, или не нашли универсальную реализацию concurrent (queue, stack,...) для большинства случаев?

                                                                                                      Полноценной Transactional Memory я так понимаю в ближайшие лет 10 ждать не стоит :) www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4514.pdf
                                                                                                        +1
                                                                                                        Чтобы сделать что-то хорошее — нужно время. На queue его просто не хватило в этой итерации языка.

                                                                                                        Transactional Memory тоже ещё не готов.

                                                                                                        На последнем собрании обсуждались различные проблемы TM. Одна из основных — мало кто реализовал в компиляторе TM и мало кто пользуется. Авторы предложения хотели получить намного больше фидбека.

                                                                                                        Отдельная проблема — это что TM добавляет накладных расходов, когда не используется транзакционность. Это не похоже на C++, и требуется доработка.

                                                                                                        Полные логи совещания доступны тут.

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

                                                                                              Самое читаемое