Комментарии 177
скоро::программировать<'на'> &зыке << !С++ ^будут* << _так?;
Например, обязан ли язык различать Á (U+00C1) и Á (U+0041 U+0301)? Python различает, но приведение всех идентификаторов к NKFC может быть дороговато (в компилятор втягивать что-то размера ICU, причём на этапе, где лексер и так отрабатывает много специфики языка — см. например Clang — где есть лексический парсинг обычного кода, строк препроцессора, кода внутри блока `#if 0`, и т.п.) А вот Go ничего не делает с этим — и можно получить злобные проблемы от внешне одинаковых идентификаторов в разных местах, так, что редактор этого не покажет.
А когда апдейтится стандарт Unicode — менять таблицы вслед?
Требовать ли от внешних средств типа линкера поддержки таких идентификаторов, и что делать, если не поддерживает?
Я не согласен со многими решениями в C++, но сама идея, что это (вместе с C) всё-таки языки системного программирования, предполагает, что выход за пределы базовых доступных везде средств должен выполняться очень ограниченно и осмотрительно.
Ведь даже наличие ASCII, считаем, стало обязательным только с C++17 — я имею в виду отказ от триграфов, которые применялись для возможности написания на C++ в странных местах типа zSeries с локализованными вариантами EBCDIC…
И компиляторы, хотя и не все, давно позволяют называет идентификаторы по русски. Это вопрос к программистам, а не к стандарту сегодня, на самом деле.
почему бы в C++ не принять эту фичуА это от программистов зависит, компиляторы её ужа давно поддерживают.
<
sarcasm>особенно golang</sarcasm>
Или Вы знаете другой язык из популярных, где это было раньше?
if (y = f(x), y > x) {
... // statements involving x and y
}
Новая форма аналогична созданию блока, в котором определены новые переменные, а после этого вложен собственно if. Пример автора статьи:
if (auto it = m.find(key); it != m.end()) { .... }
аналогичен
{ auto if = m.find(key); if (it != m.end()) { .... } // кстати, тут может быть else-ветка, или даже цепочка else if ... else, // в которой эта переменная будет видна }
но блок не выписывается явно.
По выходу из блока сработают деструкторы; кроме того, новая переменная не будет видна после завершения if — чтобы случайно её не применить где не следует.
Потому — это чисто «сахар». Но очень практически полезный, раз ввели.
Кстати, насколько я понял final draft, несколько отдельных init-statement ввести нельзя. Немного жаль.
Был бы другой образец — взяли бы его, потому что ничто не мешало, например, создать отдельное ключевое слово и блок за ним (что было бы как-то более понятно при отсутствии такого образца), или сделать другое построение блока (хм, а почему if и switch, но не while?), перетащить GCC вариант ({...}) с уточнением блочного контекста, и т.п.
for(int i=f(x); i<10;) {;;}
распространение этого синтаксиса и на if является вполне логичным продолжением даже без влияния go. Тот факт, что что-то в новом C++ похоже на go ещё не означает, что они двигаются в одном направлении и даже не значит, что именно оттуда оно заимствуется:
Цитирую пропозал:
There are three statements in C++, if, for and while, which are all variations on a theme. We propose to make the picture more complete by adding a new form of if statement.
Для меня тут колоссальной разницей является то, что в for три части присутствуют обязательно (даже если пустые), а в if первая может отсутствовать. Именно конкретный метод решения был взят в конкретном месте. Альтернатива, в виде названного в том же пропозале with(), присутствовала в других местах.
И я не могу назвать его «логичным продолжением», больше похоже на достаточно злобный хак — хотя бы потому, что усложняет грамматику.
> There are three statements in C++, if, for and while, which are all variations on a theme.
И он тут же исключает из рассмотрения else для if… мне эта логика ой не кажется корректной.
К слову, else для for и while (по образцу Питона) иногда тоже очень полезно. :)
А Вы где-то ещё видели именно такой же синтаксис — две части через точку с запятой,А какое это имеет отношение к делу? После не значит в следствии. Мысль о том, что if не хватает инициализации посещала меня ещё до появления golang. Да и сами авторы пропозала тоже мотивируют внутренними потребностями языка.
auto res1 = std::to_chars(arr, arr + 128, 3.14f);
Может имело бы смысл его записать в духе современного C++?auto res1 = std::to_chars(std::begin(arr), std::end(arr), 3.14f);
Как-то понадежнее будет.Тогда бы и тут можно было писать что-то типа
auto [ptr, error] = std::to_chars(...)
Меня больше бомбануло от
struct to_chars_result
{
char* ptr;
std::errc ec;
};
На вход итераторы, а на выход — поинтер… Слов нет, одни выражения. А, ну и голый эррор код. С++ так и не смог нормальную единую обработку ошибок, сплошные костыли.
Другое дело, что к to_chars_result::ptr можно получить доступ даже если в to_chars_result::ec находится код ошибки… Вот это не есть хорошо. Какой-нибудь std::variant<char*, std::errc> был бы, наверное, уместнее. Но, вероятно, с точки зрения эффективности to_chars_result обходится дешевле.
Есть интересный proposal как раз на эту тему http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0650r0.pdf
Ждём, верим :)
А что в них настолько плохого, что от них нужно обязательно избавиться?
PS Интересно, а что мешает писать их не используя?
Многомегабайтные заголовки сами по себе не такая большая беда. Компиляторы довольно давно научились это дело кешировать. Две гораздо большие проблемы — война макросов и свалка определений.
Война макросов, как можно догадаться — конфликт и наложение эффектов от макросов, определённых в разных заголовках.
Свалка определений — тащим одну маленькую шаблонную функцию, а получаем в область видимости пол-буста вместе с MPL. К тому же, приводит к необходимости тащить вместе с библиотекой заголовки всех её зависимостей (и правильно их раскладывать по местам), которые отметились в заголовках самой библиотеки. И транзитивные в том числе.
В C# уже завезли RAII и нативную компиляцию?
Да, в C# с первых версий завезена RAII, просто он требует большего числа ручных действий.
А насколько он сравним с С++ по эффективности генерируемого кода?
Некоторые функции, близкие к границе, уже не попадут в «горячие», как могли бы при полной компиляции под целевую архитектуру или при jit-компиляции.Понятно, что сборку строго под конкретную платформу вы не победите.
С JIT'ом же всё не так однозначно: да, он лучше отбирает функции, чем рачная подстройка — но и ресурсов он на себя отбирает больше! Не факт, что в результате выигрыш получится.
То есть создать бенчмарк под любой JIT, чтобы показать его «крутизну» — не проблема. А вот в реальных приложениях… не факт, ой как не факт…
Когда уже откажутся от header-файлов?
Когда завезут модульную систему
Свалка определений — тащим одну маленькую шаблонную функцию, а получаем в область видимости пол-буста вместе с MPL.
Я просто перешел на C#.
А я — никуда уходил с Delphi, там всего этого 'счастья' не было изначально.
даже если спецификатор constexpr не указан, лямбда все равно будет constexpr, если это возможно
Зачем же тогда указывать constexpr? Явная декларация о намерениях? Мне кажется, С++ движется в этом смысле в сторону питона — explicit is better than implicit.
auto[iter, ok] = mySet.insert(42);
Python: a,b=1,2
А с чем сравнивать возврат нескольких значений — с Python, Go, Swift, Erlang, Haskell, чем-то ещё — вопрос персонального опыта. В данном случае второй ok это ближе к тому, что я видел по Go. Был бы он первым — был бы стиль Erlang :)
1. Формальный — что это именно части полного результата функции и соответственно не требуют рисования промежуточных переменных.
2. Практический — что в точно такой же манере, как современные ABI предпочитают передавать K первых параметров через регистры, чтобы гонять через RAM — результаты тоже можно передавать через регистры и не заниматься косвенным доступом.
Ну а насколько Вам это будет принципиально — не могу предсказать.
Вполне можно было вообще все функции считать constexpr по-умолчаниюНельзя. По историческим причинам. Функции не описанные как
constexpr
компилятор, в большинстве случаев, в нужном контексте просто не видит (раздельная компиляция, всё такое). Так что constexpr
таки нужен.Но можно было бы всё
inline
-функции сделать constexpr
— это правда…Нельзя. По историческим причинам. Функции не описанные как constexpr компилятор, в большинстве случаев, в нужном контексте просто не видит (раздельная компиляция, всё такое).
Не могли бы вы объяснить по-подробнее? Почему прям нельзя?
Допустим, не видит компилятор тела функции из библиотеки — ну и ладно, в рантайме вызовем. В конце концов, constexpr не гарантирует выполнения на этапе компиляции.
Другое дело, что время компиляции от этого, скорее всего, сильно выросло бы.
В конце концов, constexpr не гарантирует выполнения на этапе компиляции.В случае с описанием переменной (а также использования в качестве параметра типа, размера массива и в других местах) — гарантирует.
Не могли бы вы объяснить по-подробнее? Почему прям нельзя?Потому что описать
constexpr
-функцию без тела — нельзя, а без constexpr
-можно.Но реально, конечно, это всё поблажки разработчикам компиляторов:
constexpr
-функции ведь приходится интерпретировать — а для этого, фактически, отдельный транслятор нужен… Вот и ограничивают их. Вначале ограничения были совсем драконовскими, но сейчас потихоньку гайки отпускают.Если компилятор не сможет сделать constexpr, он об этом сообщит.
Если компилятор не сможет сделать constexpr, он об этом сообщит.К сожалению всё не так. Это происходит в любом случае в месте подстановки. Посмотрите сами — вызов
printf
не машает функции отрабатывать в компайл-тайме. Если он не триггерится, конечно.Ну так и нафига козе баян — в смысле нафига явно тут указывать, что функция
constexpr
? Если возможность её использовать всё равно зависит от конкретного значения?P.S. Кстати за счёт
constexpr
любой коспилятор C++ — теперь где-то ещё и инетерпретатор. clang отрабатывает раз в 8 быстрее, чем gcc…Отличная статья! Всё собрано в одном месте.
Свертка параметров шаблона (Fold expressions)Сложность понимания кода все еще очень высока. Все еще нужно разбираться в нюансах, а не читать «на лету» код
for (const auto &[key, value] : myMap)
Наконец-то не надо писать iter->first, iter->second!
Если так поступить, то C++ постигнет судьба Delphi,
который загнулся после выпуска новой версии не совместимой со старыми.
Просто не используйте ненужные элементы.
Потому всё это напоминает мне PL/1, когда обычный программист не знал большей части языка, из-за чего возникали реальные проблемы с пониманием чужого кода написанного человеком, знающим другие части языка.
Ну библиотеки не всем надо читать в обычном случае. А так есть же немало стандартов кода и линтеров, помогающих их придерживаться.
А с iostream-то что не так? Переделать его, возможно, и правда следует. Но удалять?..
std::vector<bool>
Эти классы взялись не на пустом месте, а были сделаны как абстракция понятия "поток данных". Во всех современных языках есть свои аналогичные абстракции:
Delphi 7 — TStream
Java — java.io.InputStream, java.io.OutputStream
C# — System.IO.Stream
Python — io.IOBase
Node.js — модуль stream
Поэтому без замены iostream на что-то более красивое убирать его из стандартной библиотеки нельзя.
и т.д. printf по сравнению с ними просто сказка.Ровно до тех пор, пока не придется использовать printf в обобщенном коде. Или до тех пор, пока не придется использовать printf для вывода в определенный пользователем поток данных.
Для тех, кто исходит на известную субстанцию от iostreams, уже давно есть fmtlib. Которая, среди прочего, позволяет работать с пользовательскими типами, для которых определены операторы сдвига именно в std::ostream.
Я думаю, имеется ввиду выкинуть iostreams
на мороз именно в текущем виде. Просто ради примера, в Rust абстракции байтовых потоков ввода-вывода требуют реализации по одной функции на чтение и запись, соответственно. Это в самой базовой форме. В С++ тщательно ковыряться с буфером потока и, возможно, самим классом потока.
Конечно, в С++ есть места, где сложность получается избыточной и не несёт полезной нагрузки — с ними пытаются бороться.
Я не говорю, что golang — хуже, просто это другой язык с другой установкой, идеей и целями. Не нужно все языки под golang причёсывать.
решают это проблему, но заодно вводят путаницу в вызовы конструкторов. По мне, так правильней было бы разделить назначение struct и class. И как минимум сделать дефолтный конструктор для struct по списку членов.
Истина где-то посередине. Но середина у всех разная. У меня она ближе к пункту 1.
Про разделения struct и class я говорил (увы не уточнил) в контексте, как надо было сделать, когда вводились классы. Сейчас, понятно, это сделать уже нельзя.
(void)param2;
? Это ему более удобная замена. Кроме условной компиляции такое бывает необходимо при перегрузке виртуальных функций, мб ещё где…C++ и так сделал всё необходимое для сосуществования — обертки над C++ доступны практически во всех языках.
Скоро найти человека, который знает весь синтаксис С++ будет нереально. И это меня печалит.
что значит «излишняя» для вас?
Думаю, очевидно, что для меня как переводчика это лишняя заморочка.
Так-то до ассемблера можно дойти.
А что Вы имеете против ассемблера?
Вспомнил, нпр., такое утверждение:
Есть задачи, которые компилятор на языке высокого уровня решить лучше человека не сможет, по крайней мере пока, поэтому да смогу, но пример выбирать не вам. Доказывать очевидное не буду, лучше возьмите открытые кодеки и системы распознавания образов — думаете люди от нечего делать там целые функции на ассемблере или интрисиками пишут?
Далее:
И даже дальше.
А куда дальше ассемблера?
конструкция одна из наиболее выразительных, где избыточность выражена через maybeто есть она все таки не излишняя?
А настанет ли момент, когда из языка будут удалять ненужные элементы
…
Особенно понравился [[maybe_unused]]!
… слишком много стало архитектурных излишеств
то есть, очевидно, слово «понравился» имело ироничный характер.
для меня как переводчика это лишняя заморочка.Во-первых даже для «переводчиков» эта заморочка совершенно не лишняя т.к. показывает намерение о том, что эта переменная на самом деле, вероятно, не нужна и её можно не переносить. Во-вторых, мне кажется, очевидно, что интересы переводчиков с С++ при разработке стандарта имеют крайне низкий приоритет. Гораздо важнее удобство C++ разработки.
А что Вы имеете против ассемблера?Вы специально из контекста выдёргиваете? Там речь шла о том, что ассемблер — плохой C++ (и наоборот, но речь не об этом). Соответсвенно, «излишества» — понятие относительное. И снова возвращаемся к вопросу, почему Вы считаете, что конкретно "[[maybe_unused]]" — излишество, причём самое явное в новом стандарте.
А приводить в качестве аргумента об архитектуре ЯП постановление ЦК КПСС по архитектуре зданий от 1955 года — это вообще какая-то самодискредитация. Во-первых не та предметная область. Во-вторых в строительстве нет единого понимания о том, что такое хорошо, но постановления ЦК КПСС тут точно не авторитет, также как и по отношению к генетике и кибернетике. И в третьих последствия этого решения всем нам печально известны в виде страшных как смертный грех жилых районов в городах СНГ, которые не критиковал только ленивый. Примеров того, как от этой «архитектуры» старались и стараются избавиться, везде, где представляется такая возможность — валом.
А приводить в качестве аргумента об архитектуре ЯП постановление ЦК КПСС по архитектуре зданий от 1955 года
Я сказал "архитектура", а не «архитектура ЯП»! И не приводил в качестве аргумента, а только в качестве сравнения, метафоры.
очевидно, что интересы переводчиков с С++ при разработке стандарта имеют крайне низкий приоритет.
Именно это я и говорил:
стоит больше думать о сосуществовании с другими языками
И возможно ли доказать, что удобство C++ разработки повысилось?:
Гораздо важнее удобство C++ разработки.
«Within C++ is a smaller, simpler, safer language struggling to get out.» — Bjarne Stroustrup
«Within C++ is a smaller, simpler, safer language losing struggle to get out.»
Fixed
Увы, я потихоньку разувериваюсь. Некоторые хронические болячки либо не решаются, либо решаются с адскими задержками. Зато накидывают всякой ерунды — вроде зета-функции Римана. Вот самое место в стандарте! Проблема же миграции на другие языки часто в том, что С++ несовместим ни с кем кроме С++ — причём часто только своим диалектом.
С++ несовместим ни с кем кроме С++
C++ совместим (хотя бы частично) с С, а это в эмбеддеде уже огромный плюс. Вот кто точно почти не совместим ни с кем, кроме себя — это стандартный .NET, который представляет собой вещь в себе и приносящий дикие боли при попытке подружить что-либо с нативной библиотекой (писать кучу [DllImport] та еще романтика, буэ)
И обе работают только в пределах MS Windows?
Но дотнет еще ничего, я боюсь подумать про Node.js и прочие "новомодные" фреймворки.
Эти функции были добавлены в C++ для совместимости с C, где они появились в C99.
Интересно, есть ли утилита или файл конфигурации для линтера, чтобы обеспечить строгое следование этим гайдам?
By pretending that parallelisation is simple – it has an enormous potential for unsuspecting developer trying to use it – and getting the whole project badly burned
Детали здесь
Вообще, новый стандарт никак не минорный, каким был С++14. И хотя многое из списка выглядит как синтаксический сахар — все очень востребовано и поможет заметно улучшить читаемость и не писать лишний код. В который раз убеждаюсь, что в комитете сидят очень адекватные люди. А всеми ожидаемые Networking и Modules задерживают не просто так — там слишком много всего надо учесть, чтобы потом не переделывать и не ломать совместимость.
Вообще эта проблема сущесествует уже давно, шаблонная магия неспроста называется магией. К счастью, она обычно изолированна в тех местах, куда большинство разработчиков почти не заглядывает.
Нельзя создать тривиальный для понимания и при этом универсальный (гибкий) язык, коим старается быть C++. К этому идеалу можно стремится, и C++-next + Core Guidelines как раз в этом направлении и двигаются.
универсальный (гибкий) язык
универсальный = гибкий?
Самый универсальный ЯП — это ассемблер: остальные языки на него транслируются и составляют подмножества комбинаций ассемблерных инструкций. При этом не все возможные комбинации задействованы.
И который из этих ассемблеров универсальный?
Платформу выбирают под задачу.Кто выбирает? Я не выбираю. Сейчас все задачи решаю на Intel Core i7, а было время, когда решал на ЕС-1022. А если серьезно, то существуют эмуляторы (см. вики) — можно любой ассемблер на любой достаточно мощной машине воспроизвести. Универсальность — свойство, заложенное в языке, а не ассортимент реализаций. Нпр., нет Дельфи-7 для CUDA, но возможно реализовать.
Кто выбирает? Я не выбираюЗначит, узкий спектр задач. Например, игру типа Pokemon GO логично делать на мобильных платформах, а не на Core-i7.
А если серьезно, то существуют эмуляторы (см. вики) — можно любой ассемблер на любой достаточно мощной машине воспроизвестиА что толку, если платформа (в более широком смысле, чем аппаратная) вам не позволит писать на ассемблере. Например, если требуется сделать клиентскую логику на веб-странице.
Универсальность — свойство, заложенное в языке, а не ассортимент реализацийАссемблер для ЕС-1022 и для Core-i7 — один язык, или разные?
В таком случае, ассемблеры не являются чем-то особенным, любой полный по Тьюрингу язык можно считать универсальным. Тот же javascript — пишем на нём эмулятор x86, ставим windows, qemu, запускаем эмулятор андроида — задача выполнена.
универсальный = гибкий?
Изменю утверждение:
Самый
То есть язык, который с одной стороны развивается в соответствии с веяниями программисткой моды (как c#), с другой стороны, позволяет с минимальными усилиями модифицировать программы под новые требования (как специализированные DSL).
В этом смысле, ассемблеры — наиболее «жёсткие» языки.
Удаленные возможности
Как же они на это пошли? А как же священная обратная совместимость со старым кодом и с C?
А можно познакомиться со списком удалённого?
PS по Вашим словам, похоже практики настолько мало знакомы с нововведениями, что не успевают даже заметить и прочувствовать, что что-то добавили и удалили.
А можно познакомиться со списком удалённого?Приложение С в стандарте этому посвящено.
PS по Вашим словам, похоже практики настолько мало знакомы с нововведениями, что не успевают даже заметить и прочувствовать, что что-то добавили и удалили.Не совсем так. В C++11 удалили какие-то фичи, которые были обьявлены как «устаревшие» в C++03, в C++17 — то, что было обьявлено устаревшим в C++11.
export template
удалили, который ни одним компилятором не поддерживался (хотя вру — вроде один экспериментальный всё же был). this->exteriorPortals.push_back({
groupId: i,
portalIndex : -1,
portalVertices: {},
frustumPlanes: frustumPlanes,
level : 0
});
Вот это сейчас совершенно легальный код в С++. Привет JS?
И да — это весьма полезная фича, благо она есть в C99, активно используется во многих проектах, а в режиме C++ поддерживается, например, clang'ом.
Ну не знаю. Я могу предположить, что это самодеятельность разработчиков компилятора, но как минимум gcc 6 съедает такой код и не давится.
Использование этой конструкции помогло мне очень эффективно портировать код с JS на C++.
Я могу предположить, что это самодеятельность разработчиков компилятора, но как минимум gcc 6 съедает такой код и не давится.Прекрасно. В таком случае дать ссылку на https://godbolt.org/ вас, разумеется не затруднит. Дополните ваш пример до компилируемого кода — и можно будет что-то обсуждать.
Если что, не я автор изначального комментария, но мне стало интересно и я проверил.
Это GNU'сное расширение, которое в стандарт C99 (а теперь и C++20) решили не вносить. Было принято решение использовать другой синтаксис, никакого отношения к JavaScript'у не имеющий.
То есть в GCC реализовали инициализаторы с одной стороны криво и не полностью, а с другой — со своими собственными расширениями.
Прэлестно, просто прэлестно.
struct A { int x; int y; int z; };
A a{.y = 2, .x = 1};
A b{.x = 1, .z = 2};
С чего вы взяли что они реализованы криво и не полностью?
Если вы не знали что 90-99% новых стандартов C и C++ сначала обкатываются на GCC то это тоже, просто прэлестно. И «как ни странно» рано или поздно высокий процент расширений GCC попадают в стандарт.
Constexpr if
…
До C++17 нам пришлось бы использовать SFINAE и enable_if:
Не понял, зачем?
template <typename T>
T GetValue(T t) {
return t;
}
template <typename T>
T GetValue(T *t) {
return *t;
}
int main()
{
int i = 5;
std::cout << GetValue(i) << std::endl;
std::cout << GetValue(&i) << std::endl;
}
C++17