Итоги встречи следующие: стандарт С++17 завершен и будет опубликован на следующем собрании в ноябре этого года; стандарт С++20 уже обзавелся первыми серьезными фичами — концептами (concepts), явными обобщёнными лямбда-функциями (explicit generic lambdas) — и это только начало.
Возможности нового стандарта С++17 обсуждались уже не раз, про нововведения писали на Хабре, проводили доклады на конференциях, поэтому снова их приводить здесь я не буду. Не секрет, что ключевой особенностью этого выпуска С++ стал перенос самых «вкусных» возможностей в неопределенное будущее. Что ж, теперь можно с уверенностью сказать, что многие долгожданные «фичи» переехали именно в С++20. Взятый курс на расширение stdlib никуда не делся, поэтому от C++20 можно ожидать гораздо большего и богатого набора функций.
Черновик стандарта С++20
Концепты
Многострадальные Concepts, когда-то не вошедшие в C++11, потом переделанные в виде нового предложения Concepts-Lite, наконец-то становятся частью стандарта.
По поводу краткого синтаксиса для концептов (terse syntax) комитету договориться пока не удалось; однако, обсуждение будет продолжаться в рамках С++20.
_VA_OPT_
#define LOG(msg, ...) printf(msg __VA_OPT__(,) __VA_ARGS__)
LOG("hello world") // => printf("hello world")
LOG("hello world", ) // => printf("hello world")
LOG("hello %d", n) // => printf("hello %d", n)
Явные обобщённые лямбда-функции (Explicit generic lambdas) [pdf]
[] <typename T> (T t) { /* ... */ }
Лямбда-функции были добавлены в язык в стандарте C++11, требовавшие указания конкретного типа; стандарт C++14 в свою очередь позволил объявлять параметры лямбда-функций со спецификатором типа auto:
[](auto x) { /* ... */ }
Теперь взаимодействовать с типами параметра (или параметров) станет проще — при определении лямбда-функций можно будет использовать привычный синтаксис шаблона функций:
[]<typename T>(T x) { /* ... */ }
[]<typename T>(T* p) { /* ... */ }
[]<typename T, int N>(T (&a)[N]) { /* ... */ }
shared_ptr для массивов [pdf]
shared_ptr<double[]> p = make_shared<double[]>(1024);
Тема поднималась неоднократно — например, здесь1.
Определение порядка байтов
Больше нет нужды прибегать к хитрым приёмам — на самом деле, компилятор и так всегда знал ответ, просто теперь он им сможет поделиться:
enum class endian
{
little = __ORDER_LITTLE_ENDIAN__,
big = __ORDER_BIG_ENDIAN__,
native = __BYTE_ORDER__
};
Назначенный инициализатор (designated initializer) [pdf]
struct A { int x; int y; int z; }; A b{.x = 1, .z = 2};
Инициализаторы битовых полей по умолчанию (default bit-field initializers)
struct S {int x : 8 = 42;};
Нововведение, которое ожидали еще со времен появления «in-class initialization» в C++11: теперь declarator для членов битового поля поддерживает инициализацию.
Исправлены const-qualified указатели на члены
struct X { void foo() const&; };
X{}.foo(); // this is okay
(X{}.*&X::foo)(); // ill-formed in C++17, well-formed in C++2a
Улучшенная дедукция аргумента шаблона
vector v{vector{1, 2}};
// Выведет vector<int> вместо vector<vector<int>>
TS (Technical Specifications)
Перечисленные ниже TS отныне являются частью С++17 (уже обсуждались в прошлые разы):
- Filesystem v1 [pdf] — порт boost::filesystem,
- Parallelism v1 [pdf] — благодаря этому TS, большая часть библиотеки algorithm отныне будет доступна в «параллельной» версии,
- Library Fundamentals v1 [pdf] — расширение стандартной библиотеки: std::string_view, std::optional, std::any, system_error.
Помимо этого, комитет опубликовал следующие технические спецификации, для которых компиляторы уже могут делать экспериментальные реализации (и они наверняка здесь обсуждались ранее):
- Coroutines v1 — вокруг сопрограмм было много обсуждений
- Ranges v1 — проскочили вслед за концептами, и, будем надеяться, попадут в С++20
- Networking v1 — имеет все шансы попасть в C++20; библиотека для работы с сокетами, в основе которой лежит boost::asio.
Далее рассматриваются TS, работа над которыми все ещё продолжается.
Некоторые из них — но, определенно, не все — войдут в состав С++20.
Модули (Modules) [pdf]
Модули по-прежнему находятся в разработке, однако, в их жизни случилось серьезное событие — стал доступен ранний черновик TS Модулей. В его публичной версии за прошедшее время ничего не изменилось (не считая пары мелких деталей) — т.е. модули по-прежнему основываются на дизайне Microsoft и не экспортируют макросы. По этому вопросу хотелось бы услышать мнение членов комитета, поскольку есть подозрения, что это не окончательное решение.
Concurrency v1 [pdf]
Опубликован, собирались принимать на этой встрече, однако фичи на разной стадии готовности — так что уже на следующей встрече данный TS будет частично принят в С++20. Содержит улучшения, которые сделают futures неблокирующими (в отличие от std::async), а также latches, барьеры и atomic smart pointers atomic_shared_ptr. Не вошел в С++17 по причине того, что был поздно опубликован и не получилось собрать достаточно практического опыта.
Непосредственно имплементация была вдохновлена MS Visual Studio, HPX и just::thread. Перед встречей были опасения, что новые предложения P0676 и P0701 могут отодвинуть принятие TS на неопределенный срок.
Transactional Memory v1 [pdf]
Опубликован, однако по этому TS еще совсем недавно у его же авторов оставались вопросы по части того, насколько быстрыми могут быть атомики и shared_ptr вне транзакций, если они должны с транзакцией взаимодействовать. Остается ждать разъяснений по поводу того, изменилось ли что-либо с прошлого раза.
Library Fundamentals v2
Опубликован. Нечто вспомогательное, потребуется для С++20.
Executors v1 [pdf]
В разработке. Должен войти в С++20, т.к. на него завязаны несколько других TS библиотек, связанных с гетерогенными вычислениями.
Reflection v1 [pdf]
В разработке. Есть все шансы попадания в С++20, ибо ему будет посвящена одна из грядущих встреч.
В основе TS лежат интроспекция кода и (с недавних пор) материализация (reification). По мнению комитета, TS пригодится в метапрограммировании, ставшему популярным благодаря Boost::Hana и аналогичных библиотек — а также для гетерогенных контейнеров.
Concurrency v2
В разработке на ранней стадии. Библиотека concurrent data structures, сoncurrent queues, lock-free алгоритмов и структур данных; включает в себя Hazard pointers (указатели опасности), RCU (безопасное освобождение памяти для lock-free контейнеров), atomic views. Вероятность попадания в С++20 крайне мала, т.к. находится еще на ранней стадии — при этом комитет осознает востребованность этого функционала и он, по словам автора, в самой активной разработке.
Parallelism v2
В разработке. Оказывается, за время разработки Parallelism v1 сделали не только Parallel STL для CPU, но и для GPU. Parallelism v2 —
Library Fundamentals v3
В разработке.
Contracts v1
В разработке. Будет либо TS, либо включение в стандарт С++20. Вкратце: улучшенная версия assert, которая позволяет проводить проверку пре- и пост- условий (т.е. инварианты). По мнению комитета, библиотека поможет С++ стать более безопасным языком, чтобы разработчики ПО для медицины, автомобилей, авиации и кибер-безопасности спали спокойней.
Numerics [pdf]
В разработке на ранней стадии. Кое-что из продвинутой арифметики наверняка войдет в С++20: decimal floating point, bounded types (например, fixed point types), unbounded types, multiprecision arithmetic. Должно пригодиться в работе в работе игровым разработчикам (которые, впрочем, привыкли сами писать то, что им требуется).
2D Graphics v1
На ранней стадии.
По мелочи
Идёт работа над добавлением новых функций к std::string — starts_with, ends_with.
Стоит заметить, что причиной появления сразу нескольких будущих TS стали распределенные и гетерогенные вычисления. Участники комитета понимают, что вот уже долгое время CUDA/OpenMP/OpenCL обгоняет нативный C++. В данный момент, все функции вроде std::invoke, std::async, параллельные алгоритмы и пр. предполагают, что std::thread используется исключительно на CPU; и даже несмотря на то, что черновик Executors TS содержит подвижки на этом фронте в виде включения в него новых фич, этого будет недостаточно, и работы предстоит еще очень много.
Уже продолжительное время эксперты из Google, NVidia, Codeplay и NASDAQ принимают участие в работе над черновиками грядущих TS, чтобы определить направление развития С++, и приоритеты будущего языка вам известны: Concurrency, Parallelism, Transactional Memory, и Networking.
Время покажет, что из обещанного действительно войдет в C++20, и насколько оправдаются наши надежды.
Литература
Как обычно, прошу прощения за любые допущенные неточности.
Michael Wong — What's in C++20 and the C++17 final score card
Обсуждение
2017 Toronto ISO C++ Committee Discussion Thread (Concepts in C++20; Coroutines, Ranges and Networking TSes published)
C++17: The Language Features — Nicolai Josuttis
Trip report: Summer ISO C++ standards meeting (Toronto)
Комментарии (190)
hdfan2
16.07.2017 06:57+6Улучшенная дедукция аргумента шаблона
vector v{vector{1, 2}};
// Выведет vector<int> вместо vector<vector<int>>
Вот это, честно говоря, очень странно. Если бы мне нужен был
vector<int>
, я бы так и написал vector v{1, 2}. Как тогда сделатьvector<vector>
? Какой-то геморрой на ровном месте.mapron
16.07.2017 07:25+1Согласен, так себе улучшение, даже больше ухудшение. Мне в паре мест как раз надо вектор векторов передавать, я уже был в предвкушении 17 стандарта, а тут такой облом)
mapron
16.07.2017 07:48+1Хм, а мне пришла в голову идея, может в таком случае писать
vector<vector<>> a { {1,2} };
?
yarric
16.07.2017 10:27А если мы хотим сделать вектор из вектора?
vector v{vector{1, 2}}
интуитивно эквивалентенvector v = vector{1, 2}
.mapron
16.07.2017 12:28Кстати да, вот у вектора отличаются конструкторы () и {}, пусть для
{ vector{1,2} } выведется вектор векторов, а для ( vecotr {1, 2} ) — копия исходного вектора, так мне кажется логичнее будет.
Antervis
16.07.2017 10:45+1интуитивно, запись vector v = {vecror{1,2}}; должна создавать вектор векторов.
mapron
16.07.2017 07:24+8Почитал «2D Graphics v1» предложение, надеюсь что это никогда не войдет в стандарт :) А что, давайте еще «кросплатформенный API по проигрыванию звука», «по декодированию видео», «по обучению нейронных сетей» — тоже тащить в стандарт языка. Прочитал Motivation и вот совсем не вдохновился. Другие низкоуровневые языки никак не стандартизируют работу с графикой, и им это никак не мешает.
Satus
18.07.2017 17:46Да и С++ это тоже никогда не мешало. Зачем это сейчас там, да ещё и в таком виде, непонятно.
Idot
16.07.2017 13:21+5Бл*! Столько лет прошло, а до сих пор никто даже и не планирует добавить в стандарт C++ нормальную работу со строками с поддержкой UNICODE.
khim
16.07.2017 16:52+3А смысл? Если их просто хранить и передавать нужно, то в std::string всё отлично вписывается в utf-8. А если обрабатывать — так ICU уже тоже есть и, как бы, никак от включения в стандарт не выиграет…
Antervis
17.07.2017 05:50-1назовите хотя бы один яп с нормальной поддержкой строк
TheShock
17.07.2017 06:04А что значит «нормальная» и «ненормальная»? Чем, например, ненормальная поддержка строк у JS?
Antervis
17.07.2017 07:01у них очень неконсистентный апи (да и в целом в языке). Например, replaceAll реализуется через split + join, remove — через replace, размер строки — только в символьных позициях (а не байтах utf-8, к примеру)
staticlab
19.07.2017 18:08справедливости ради, это можно сделать глобальной регуляркой:
someString.replace(/cat/g, 'dog');
Videoman
17.07.2017 13:45Вы уверены что она нормальная? Размер символа, до ECMA Script 6, был два байта, чего достаточно только для представления символов UNICODE 0-65536 (0 — CodePage). В ECMA Script 6 был введен целый новый зоопарк
костылейметодов для работы с CodePoint-ами из расширенного диапазона. А старые методы теперь перемещаются по CodeUnit-ам (читай кускам полных символов). Весть тот же геморой что и в С++ присутствует и там, с тем лишь осложнением, что у вас нет выбора размера CodeUnit-a и нет UTF-8, сомого оптимального представления UNICODE, на мой взгляд.TheShock
17.07.2017 18:11Я не очень разбираюсь, потому и спросил. Я помню, в 4-м пхп была отвратная работа с Уникодом, приходилось извращаться. В JS никогда об этом не задумывался, потому и решил, что в нем норм все)
В ECMA Script 6 был введен целый новый зоопарк костылей методов
Это каких?Videoman
18.07.2017 00:22+1«Костыли» — я употребил в том смысле, что, фактически, появляется новый набор методов, который с переменным успехом пытается исправить поддержку Юникода в JS. Например свойство length выдает не размер в символах, а размер в code unit-ах (16 битных). charAt() принимает индекс в code unit-ах и легко может попасть на середину code point-a и вернуть неверный код. На замену ему введен метод codePointAt(), который выдает целый code-point (учитывая суррогатные пары), но вот индекс по прежнему задается в code unit-ах. Для итерации по code point-ам теперь предлагается делать так:
const cnt = str.length; for (const i = 0; i < cnt; ++i) { const ch = str.codePointAt(i); // ... if (str.charAt(i) != ch) ++i; }
Понятно, почему так получается, но как-то это далеко от элегантности, на мой взгляд.
Videoman
18.07.2017 00:27Также место fromCharCode() появился fromCodePoint(), а учитывая как «много» людей «понимают» как устроен UNICODE и различные кодировки, думаю ясности это не прибавит.
iga2iga
16.07.2017 15:20+4Вместо того чтобы довести плюсы до красивого, лаконичного и самое главное понятного языка, лепят очередные обертки над обертками оберток…
NeoCode
16.07.2017 16:00+1Да пускай лепят. И даже чем больше тем лучше, интересно же когда что-то новенькое… Я давно уже рассматриваю это не как идеал, а лишь как одну из экспериментальных площадок, на которой можно в том числе выяснить «как не надо делать». А в практическом программировании то что не нравится вполне можно обходить стороной:)
По хорошему давно уже надо создавать новый язык «с нуля» на замену С++. С учетом опыта не только С++, но и скажем C#, Java, Objective C, D, Go, Rust, Swift и некоторых других.TheShock
16.07.2017 16:52-1А чем D плох как «язык с нуля на замену С++»?
NeoCode
16.07.2017 17:19+1Да тоже неидеален. Мне не понравилось например отсутствие явных пространств имен/модулей (как в C++/C#). Вместо этого сделана дурацкая привязка имени файла к имени модуля как в Java.
TheShock
16.07.2017 17:44+2Вы же понимаете, что это дело вкуса, уверен есть сторонники каждого из решений.
a-tk
17.07.2017 21:43Например вот этим:
atomic!"+=" (*value, 1)
А ещё const, immutable, in — разные, но так похожи.a-tk
17.07.2017 22:05Да, ещё enum забыл.
TheShock
18.07.2017 00:31Простите, не знаю ни С++ ни D, просто любопытствую. Объясните, пожалуйста, подробнее
a-tk
18.07.2017 11:24+1enum — объект-константа, который совсем-совсем константа, то есть в течение времени жизни не может измениться;
immutable, const — это разные степени гарантий неизменяемости типа,
in может писаться с аргументом функции и тоже ещё одна гарантия неизменяемости, но уже аргумента функции.
Все они имеют разную силу, и потребность в такой градации мне кажется избыточной.TheShock
18.07.2017 12:39+1Ну immutable и const ведь совершенно о разных вещах! const говорит о самой переменной, а immutable — о том, что в ней.
А как сюда енум приплести — я вообще не представляю. Еще огурцов и темной материи не хватает.
Все они имеют разную силу, и потребность в такой градации мне кажется избыточной.
Это не градация, это разные вещи. Вот стол твердый, деревянный и тяжелый. Это ведь не градации одного понятия, а разные понятия. Так и тут.a-tk
18.07.2017 14:31Правильно, о разном, но весьма похоже. Как по мне — излишне.
oYASo
18.07.2017 18:11Ничего излишнего тут нет.
Мы можем иметь константную функцию, то есть функцию, в работе которой не изменяются члены класса. И можем иметь мутабельную переменную, то есть такую переменную, которую мы можем изменить в константной функции. Это крайне полезная фича для всяких счетчиков, каких-либо флагов объекта, мьютексов и т.д., иначе бы мы просто не смогли пользоваться константными функциями.
Так что вам правильно сказали, вы сравниваете теплое с мягким.
TheShock
18.07.2017 20:00Правильно, о разном, но весьма похоже. Как по мне — излишне
Они настолько же похожи как «деревянный» и «тяжелый», какой из двух — лишний?
yarric
18.07.2017 12:14+2В D сборщик мусора вместо RAII.
TheShock
18.07.2017 12:40Я думал, там можно и сборщиком пользоваться и классической ручной уборкой.
khim
18.07.2017 13:12+3Без сборщика мусора можно только если стандартную библиотеку не использовать. Кто будет это заниматься? Разработчики ядра ОС? Ну таких пока вроде не нашлось…
yarric
19.07.2017 00:29RAII лучше, чем ручная уборка.
PsyHaSTe
19.07.2017 15:18+2Без механизма как в Rust RAII никаким образом не замена GC
Antervis
19.07.2017 16:19без какого именно механизма «как в Rust»?
PsyHaSTe
19.07.2017 16:21+1Ну, очевидно, владения и времени жизни.
Antervis
19.07.2017 17:04«механизм владения и времени жизни» и есть RAII
PsyHaSTe
19.07.2017 17:09Ну насколько я понимаю, RAII говорит про инициализацию ресурса, а не про его дальнейшее использование. Например, я хочу создать новый ресурс и вернуть его из функции. Разрушать его нельзя. При этом RAII работает: получая ресурс я его инициализировал и вернул функции выше. Про момент его уничтожения и очистки тут ничего не сказано. Хотя в доке вроде сказано что-то в про уничтожение
RAII guarantees that the resource is available to any function that may access the object (resource availability is a class invariant, eliminating redundant runtime tests). It also guarantees that all resources are released when the lifetime of their controlling object ends, in reverse order of acquisition.
Надо побольше поизучать вопрос
Antervis
19.07.2017 17:41в с++ ничто не мешает вам вернуть unique_pt/shared_ptr на подконтрольный объект.
PsyHaSTe
19.07.2017 17:50+1Rust это и есть вынесение best practice на уровень языка, без которых компилятор не пропустит код. В этом собственно и плюс языка.
khim
19.07.2017 18:01И в этом же — его минус. В C++ подобные вещи делаются на уровне библиотеки и, соотвественно, их можно менять. Когда-то best practice — это был auto_ptr, но потом выяснилось, что он небезопасен и появились unique_pte и shared_ptr. А ещё через 10 лет — что-то ещё может появиться.
А в rust — жёстко заложены сегодняшние best practice — что с одной стороны значит что пользоваться ими удобнее (всё-таки часть языка, не библиотеки), а с другой — значит что поменять их будет гораздо сложнее.
Боюсь оценить эти эффекты и увидеть какой из них более весом мы сможем лет через 10, не раньше…PsyHaSTe
19.07.2017 18:24+2Согласен, но в этом ведь нет ничего плохого. Почему бы не менять язык раз в 10 лет, когда выясняется, что есть еще более "best practice" и в новом языке они как раз реализованы? Слава богу, мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем, lock на конкретном языке потому что на нем что-то написано остается бичом, но только в достаточно редких случаях кровавых энетрпрайзов 20+летней давности.
Лично я с растом когда знакомился, было сложно, но понятно. Но когда я вижу подобные вещи
(X{}.*&X::foo)(); // ill-formed in C++17, well-formed in C++2a
То мне непонятно, плакать или смеяться… Делать по языку каждый год не вариант. Но пихать все подряд в язык на протяжении полусотни лет и надеяться на стандартную библиотеку имхо вторая крайность, не лучше первой.
Free_ze
19.07.2017 18:35+2Почему бы не менять язык раз в 10 лет, когда выясняется, что есть еще более «best practice» и в новом языке они как раз реализованы?.. мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем
А правки вносить методом переписывания целых модулей? Какую пользу это принесет бизнесу в обозримой перспективе?PsyHaSTe
19.07.2017 19:39Зачем переписывать? Обычно переход происходит постепенно, по модулям, да. От появления нового языка старый не исчезает в вспышке адского пламени, а вполне себе сосуществует.
Free_ze
19.07.2017 20:02Зачем переписывать?
Чтобы правку сделать, очевидно.
От появления нового языка старый не исчезает в вспышке адского пламени, а вполне себе сосуществует.
То есть для одного и того же проекта вам нужно не n специалистов, а 2n. То есть одни будут поддерживать, другие — мигрировать, третьи — тестировать совместимость…Зачем? Да прост есть один прикольный язык, на хабре писали, что он — убийца C++...DistortNeo
19.07.2017 20:08+1А потом вдруг конкуренты выкатят продукт, который, благодаря тому, что написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку.
khim
19.07.2017 20:12А потом вдруг конкуренты выкатят продукт, который, благодаря тому, что написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку.
Примеры — в студию. Я знаю случаи, когда новый продукт замещал старый из-за того, что он был по-другому устроен, но ни разу, никогда и ни при каких условиях не видел, чтобы кто-то выиграл только за счёт того, что новый продукт «написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку».
В большинстве случаев использование другого языка в успехе продукта — дело десятое. За исключением случаев когда старый продукт по тем или иным причинам становится недоступен (как с J++, скажем, произошло или с Visual Basic'ом).
Free_ze
19.07.2017 22:44Что если мы бросим ресурсы на никому ненужный хипстерский язык, а конкуренты во всю будут пилить киллер-фичи? Мне кажется, что этот «вдруг» вероятнее.
PsyHaSTe
19.07.2017 22:57khim
19.07.2017 23:55-1Это хороший пример. Только «читать» нужно не то, что там написано, а то, о чём там не написано.
Ибо после того, как возможности переросли масштабы, которые смогла поддерживать небольшая первоначальная команда, Yahoo Store потерял лидерство (Shopify, eBay и даже Amazon сейчас имеют больший оборот) и тот факт, что «в Yahoo на серверах работает софт, написанный на всех пяти языках, которые советует хакерам Эрик Реймонд» их не спас, как ни удивительно.
Так что непохоже, что подход Самыми безопасными были те, кому требовались специалисты по Oracle. О таких не стоило беспокоиться. Также мы были спокойны, если требовались разработчики на C++ или Java. сработал.
На короткой дистанции — да, «хипстерские языки» (вернее, на самом деле, уникальные разработкичи хорошо интегированные с «хипстерскими языками») могут показать неплохую отдачу, но на длинной «специалисты по Oracle и разработчики на C++ или Java» берут верх. О чём, собственно, и речь.
PsyHaSTe
19.07.2017 21:28+1То есть если вы пишете код, который взаимодействует с базой, то вы нанимаете специалиста по SQL? Мне казалось, это базовый навык для бекенд-разработчика. Точно так же никто не мешает знать параллельно C++ и Rust, тем более, что после пятого языка каждый последующий можно изучить ну буквально за неделю. Остается еще стандартная библиотека, но она как правило осваивается методом гугления.
Free_ze
19.07.2017 22:33+2То есть если вы пишете код, который взаимодействует с базой, то вы нанимаете специалиста по SQL?
В нормальных местах, кстати, бывает специальный дядька, который проектирует базу, пишет хранимки и т.п. (DBA называется), т.е. разработчик бизнес-логики может знать SQL так же, как и фронтовую часть — в общих чертах. Но так как бизнес повально экономит, и бизнес-логика редко бывает сложной в типовых проектах, то появились сердитые «фуллстеки». Стоит ли говорить, что у миддла-фуллстека оклад в среднем выше, чем у такого же узкого специалиста, а качество работы хуже в каждой отдельно взятой дисциплине?
после пятого языка каждый последующий можно изучить ну буквально за неделю
Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=
Читывал я и код джависта на C++, так и C# с венгеркой от сишника. Нафиг таких SO-умельцев, скажу я вам (= Прогресс подарил нам разделение труда, но некоторые программисты почему-то считают себя выше этой ерунды.PsyHaSTe
19.07.2017 22:56+3В нормальных местах, кстати, бывает специальный дядька, который проектирует базу, пишет хранимки и т.п. (DBA называется), т.е. разработчик бизнес-логики может знать SQL так же, как и фронтовую часть — в общих чертах. Но так как бизнес повально экономит, и бизнес-логика редко бывает сложной в типовых проектах, то появились сердитые «фуллстеки». Стоит ли говорить, что у миддла-фуллстека оклад в среднем выше, чем у такого же узкого специалиста, а качество работы хуже в каждой отдельно взятой дисциплине?
Во-первых как правильно сказано, для большинства проектов отдельный DBA это просто оверкилл. Все же нужно исходить из реальных потребностей, а не "у меня 1000 серверов, поэтому сайт-визитка должен иметь такой же 30-этапный папйлайн деплоя, как и у меня. Что, у вас нет отдельного бекенд-разработчика для врапперов над DALом? Фу, что за непрофессинальность". Я раньше как раз фуллстечил, от SQL и до css, щас больше осел в беке. Только база все равно основополагающее, и знать надо, никуда не деться.
Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=
Читывал я и код джависта на C++, так и C# с венгеркой от сишника. Нафиг таких SO-умельцев, скажу я вам (= Прогресс подарил нам разделение труда, но некоторые программисты почему-то считают себя выше этой ерунды.Нет, я только за разделение. Только ваша позиция человека, который привык к молотку, и вместо того, чтобы научиться пользоваться еще и отверткой всеми силами пытается доработать молоток, чтоб он еще и завинчивать умел. Я-то как раз за разделение, в данном случае — инструментов. А то получается, что вместо отдельного SQL для баз и отдельного языка общего назначения встроили бы язык запросов прямо в плюсы и зажили бы. Хотя в шарпе так и сделали, и работает весьма неплохо… Мда, плохой пример привел. Но все равно мысли примерно в таком духе.
Кстати, что касается цитаты:
Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=
Это ведь скорее минус языка, а не плюс. Тот же шарп или го за неделю изучить можно легко. И это как раз следствие бесконечного расширения. Проблема существующих языках в решениях дизайна, которые были приняты давным-давно, себя не оправдали, но их тянут из соображений обратной совместимости. Тот же раст по-сути это те же плюсы с небольшим сахаром, нормальным пакетным менеджером, и выкинутыми костылями. Ну буквально все улучшили, что не удивительно, за 50 лет хоть 1 язык должен был такого достичь. Но все равно "не тру" почему-то по мнению людей.
khim
19.07.2017 23:59Тот же шарп или го за неделю изучить можно легко.
Нельзя их изучить за неделю. Чтобы сделать простенькую утилиту — может быть. А чтобы начать писать идеоматичный код, а не, как в пресловутой притче, «FORTRAN на любом языке» — требуется время сравнимое с изучением C++ со всеми его наворотами.
Но все равно «не тру» почему-то по мнению людей.
Потому что это — другой язык с совсем другой экосистемой. Неясно — сможет ли он вообще набрать критическую массу и ещё менее ясно — будут ли на него переходить с C/C++…PsyHaSTe
20.07.2017 12:03Нельзя их изучить за неделю
Я считаю иначе.
Потому что это — другой язык с совсем другой экосистемой. Неясно — сможет ли он вообще набрать критическую массу и ещё менее ясно — будут ли на него переходить с C/C++…
С99 От С++17 отличается сильнее, чем С++ от раста. И ничего, переходят люди как-то.
0xd34df00d
20.07.2017 23:24Это ведь скорее минус языка, а не плюс. Тот же шарп или го за неделю изучить можно легко. И это как раз следствие бесконечного расширения. Проблема существующих языках в решениях дизайна, которые были приняты давным-давно, себя не оправдали, но их тянут из соображений обратной совместимости. Тот же раст по-сути это те же плюсы с небольшим сахаром, нормальным пакетным менеджером, и выкинутыми костылями.
Как насчёт хаскеля?
PsyHaSTe
21.07.2017 12:09+1Хаскель не императивный, поэтому как замена императивного С/С++ плохо годится, и у него самого хватает проблем и без этого.
0xd34df00d
21.07.2017 19:24Хаскель не императивный, поэтому как замена императивного С/С++
Это совершенно неочевидная импликация. В плюсах ценна близость к железу, а не императивность, и возможность через эту самую близость выразить некоторый алгоритм чуть более эффективно. Вы вряд ли захотите писать на императивном питоне то, что имеет смысл писать на таком же императивном C++.
Олсо, когда пишут C/C++ через слеш, в мире умирает один котик. У них сильно разные области применения.
и у него самого хватает проблем и без этого
Каких?
Стрипы про ленивость — это хорошо, конечно, и я согласен, что рассуждать о производительности ленивого кода сложнее, чем о производительности строгого, но
возьмите Idris, он строгий по умолчанию, во-первых, есть{-# LANGUAGE Strict #-}
, во-вторых, рассуждать о производительности и корректности плюсового кода тоже не столь тривиально, скажем.
Free_ze
21.07.2017 12:49SQL — это DSL, который заменить не так-то просто. А тредик начинался про смену шила на мыло.
Это ведь скорее минус языка, а не плюс. Тот же шарп или го за неделю изучить можно легко. И это как раз следствие бесконечного расширения.
Не только в расширении дело. Уровень языков разный. C++ — это, по сути, поумневший компилятор C. При этом C синтаксически достаточно простой язык, но при этом писать на нем относительно сложно. А тот же C# уже так разнесло, что спека на него по размеру стремительно приближается с C++, плодя неиспользуемое легаси и просто сомнительные фичи как не в себя.PsyHaSTe
21.07.2017 12:55+1Использую все фичи C# 7.0, кроме разве что ref locals/ref returns. Можно пример фич, которые никому не нужны? Просто по моему опыту наоборот, есть 100500 миллионов issues на гитхабе, которые просят реальные люди, и их делают не от скуки, а потому что они дают вполне конкретную ценность. И команде приходится выбирать, что делать, а что нет. А не то, что пихают все подряд чисто чтобы хоть чем-то заняться.
Free_ze
21.07.2017 13:21+1Можно пример фич, которые никому не нужны?
Навскидку: устаревший синтаксис создания объектов делегатов, сам класс Delegate, необобщенные коллекции, кортежи, out без предварительного объявления, импорт статических классов.
есть 100500 миллионов issues на гитхабе, которые просят реальные люди, и их делают не от скуки, а потому что они дают вполне конкретную ценность.
Именно поэтому большую часть из этого треша отклоняют?) Да, им это безумно нужно, но не факт, что остальные были бы рады читать такой код. Посмотрите на Java — это, конечно, другая неадекватная крайность, но и в таком консерватизме есть некоторый смысл.PsyHaSTe
21.07.2017 13:46+3Навскидку: устаревший синтаксис создания объектов делегатов
"Устаревший" синтаксис делегатов полезен, когда не надо перечислять все аргументы функции. Типичный пример:
ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };
сам класс Delegate
А что с ним не так?
необобщенные коллекции
При чем тут язык?
кортежи, out без предварительного объявления
Крайне полезные фичи. Таплы просто кучу мест заменили, где есть приватный класс из двух полей, потому что нам нужно вернуть 2 значения. out без объявления как бы тоже логично, потому что единственная задача out-а это заполнить значение пустой переменной. Очень бесило, что нельзя просто написать
while (!tryparse(out x) && x > 3)
однострочником, просто потому что на предыдущей строке нужно объявление переменной.
импорт статических классов
Ну видимо вы никогда не писали код, который активно использует фабрики или математику. Пример из моего репозитория: попробуйте убрать
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
и посмотрите, насколько классным и читаемым стал код.
Именно поэтому большую часть из этого треша отклоняют?) Да, им это безумно нужно, но не факт, что остальные были бы рады читать такой код. Посмотрите на Java — это, конечно, другая неадекватная крайность, но и в таком консерватизме есть некоторый смысл.
Из всех фич, что предложили, я считаю бредом только ref locals, хотя бы потому что GetNumber(5) = 10 выглядит очень странно. Но 1 фича из десятков, которые очень нужны и полезны выглядит не очень большой ценой, тем более что она меня никак не задевает.
Free_ze
21.07.2017 14:19Типичный пример
Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?
При чем тут язык?
По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.
Крайне полезные фичи.Таплы просто кучу мест заменили, где есть приватный класс из двух полей, потому что нам нужно вернуть 2 значения.
Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.
out без объявления как бы тоже логично, потому что единственная задача out-а это заполнить значение пустой переменной
Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.
Ну видимо вы никогда не писали код, который активно использует фабрики или математику. Пример из моего репозитория:...
using Sf = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
Но 1 фича из десятков, которые очень нужны и полезны выглядит не очень большой ценой, тем более что она меня никак не задевает.
«100500 миллионов» превращаются в десятки и вы признаете, что мусор плавно просачивается в язык.DistortNeo
21.07.2017 15:24Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?
Вы привязываетесь к частностям.
По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.
Во-первых, это не синтаксис, они совершенно никому не мешают.
А во-вторых, предлагаете отказаться от обратной совместимости?
Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.
И не надо их передавать наружу. Используйте их локально там, где это удобно, а где надо выдавать наружу — заведите отдельный класс.
Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.
Почему же исключительный случай? Сделал ради интереса поиск в своём коде по
TryGetValue
иTryParse
— абсолютно во всех случаях строкой выше было объявление переменной, которое можно перенести внутрь вызова функции, причём в видеout var x
, без указания конкретного типа, а это экономия до 50 символов!Free_ze
21.07.2017 15:43+1Вы привязываетесь к частностям.
Без такой частности пример теряет смысл. Если параметры можно игнорировать, то зачем они нужны? Примерно так же в пользу goto можно приводить 4-5 вложенных цикла и говорить, что это решает все наши проблемы.
Во-первых, это не синтаксис, они совершенно никому не мешают.
А во-вторых, предлагаете отказаться от обратной совместимости?
Это был один из примеров накопления легаси в отдельно взятом C#.
И не надо их передавать наружу. Используйте их локально там, где это удобно.
Чтобы не передавать данные наружу уже есть анонимные типы.
Почему же исключительный случай?
Очевидно, других подобных конструкций грамматика языка не допускает.DistortNeo
21.07.2017 15:53Без такой частности пример теряет смысл. Если параметры можно игнорировать, то зачем они нужны?
А функциям, в определении которых присутствуют параметры по умолчанию, вы так же плохо относитесь?
Примерно так же в пользу goto можно приводить 4-5 вложенных цикла и говорить, что это решает все наши проблемы.
А ещё есть люди, которые утверждают, что использование
return
эквивалентноgoto
, и чтоreturn
должен быть только один — в конце функции.
Это был один из примеров накопления легаси в отдельно взятом C#.
Да, это плохо.
Ещё ещё один плохой пример легаси, который раздражает меня гораздо больше — типы-перечисления (enum) не реализовываютIEquatable<T>
. Казалось бы, добавить интерфейс — не проблема, но это может поломать старый код.
Чтобы не передавать данные наружу уже есть анонимные типы.
Но вы не можете использовать их в объявлении приватной функции, а вот кортежи — можете.
Очевидно, других подобных конструкций грамматика языка не допускает.
Вы можете использовать
out
параметры где угодно. Просто есть типичный usecase для них.
PsyHaSTe
21.07.2017 15:32Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?
Это пример кода, а не дизайна.
По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.
Не поленился, залез в спеку, нашел там только упоминания
IEnumerable<T>
(потому что компилятор специально обрабатывает yield) иIList<T>
(потому что компилятор должен уметь преобразовывать в него такой базовый тип, как массив). Всё. Больше никаких упоминаний, тем более не-дженериков, кроме как в примерах нет.
Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.
Приватный метод, который должен вернуть пару значений. Вот опять же пример из существующей кодовой базы:
private IEnumerable<(string AgName, string PrimaryServer, string SecondaryServer)> GetAvailabilityGroupsOn(string serverName) { var connectionString = GetConnectionString(serverName); using (var connection = new SqlConnection(connectionString)) { connection.Open(); var agQuery = new SqlCommand(AgQueryText, connection); using (var reader = agQuery.ExecuteReader()) { while (reader.Read()) { yield return (reader.GetString(0), reader.GetString(1), reader.GetString(2)); } } } }
Метод этот используется один-единственный раз в LINQ-запросе
var availabilityGroups = databaseServers.SelectMany(GetAvailabilityGroupsOn).Distinct().ToList();
Что, ради этого класс городить? Или обычный тапл использовать и потом с Item1/Item2 возиться?
Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.
Так единственный случай, когда out используется по назначению это TryXXX операции. Не считая интеропа.
using Sf = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
Ну так замените. Что, стало читабельнее? Понятнее? Как по мне, так нет. Просто бесполезный суффикс, "чтобы было как всегда было".
«100500 миллионов» превращаются в десятки и вы признаете, что мусор плавно просачивается в язык.
У меня диаметрально противоположенный вывод, из 100500 миллионов просачиваются единичные золотые предложения.
Free_ze
21.07.2017 16:05-1Не поленился, залез в спеку, нашел там только упоминания IEnumerable<T>… Всё. Больше никаких упоминаний, тем более не-дженериков, кроме как в примерах нет.
Да, согласен, почему-то я считал иначе (как это у C++ заведено).
Приватный метод, который должен вернуть пару значений. Вот опять же пример из существующей кодовой базы:
Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.
Ну так замените. Что, стало читабельнее? Понятнее? Как по мне, так нет. Просто бесполезный суффикс, «чтобы было как всегда было».
Это заметно снижает уровень драмы) Под соусом новизны подаётся то, что в том же JavaScript считается плохим дизайном и старательно изгоняется из языка.
Лучше бы разрешили свободные функции и вопроса бы не стояло, а так статический импорт — это просто костылик.DistortNeo
21.07.2017 16:32Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.
Предлагаете
yield return (reader.GetString(0), reader.GetString(1), reader.GetString(2));
заменить наyield return ProcessResult(reader)
, гдеProcessResult
— передаваемая параметром лямбда, возвращающая анонимный тип?
Да, такое, в принципе, возможно. Но нужно смотреть на остальную архитектуру кода.
PsyHaSTe
21.07.2017 16:46+1Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.
Он не имеет смысла в другом контексте. Вот у вас есть код, который использует анонимные классы? Наверняка есть. Теперь предположим, что он используется в одном методе в трех разных местах. Что хочется сделать? Правильно, вынести функцию. И вот тут получается, что нельзя просто так вынести функцию, которая возвращает анонимный тип. А кортеж — можно. В итоге, как только хотим вернуть более 1 значения из функции начинаем засорять глобальное пространство имен ничего не значащими DbScanResult, StorePaymentPair и прочими, вся суть которых просто в том, чтобы передать 2 значения.
Это заметно снижает уровень драмы) Под соусом новизны подаётся то, что в том же JavaScript считается плохим дизайном и старательно изгоняется из языка.
Лучше бы разрешили свободные функции и вопроса бы не стояло, а так статический импорт — это просто костылик.Это добавляет информационный шум безо всякой ценности. Там около 200 использований функций этого класса в коде, добавили 3 символа, итого просто +600 символов на ровном месте. Кому они были нужны? Зачем? Что хорошего они добавили? Непонятно.
Free_ze
21.07.2017 17:23Он не имеет смысла в другом контексте. Вот у вас есть код, который использует анонимные классы? Наверняка есть. Теперь предположим, что он используется в одном методе в трех разных местах. Что хочется сделать?
Если что-то используется больше одного раза (не создание-вызов-получение, а действительно похожие использования), то я бы вопроса такого не ставил — отдельный тип. Чтобы был явный контракт, с отслеживаемыми юзаджами и прочим.
Это добавляет информационный шум безо всякой ценности.
Вы считаете, что набор полей без общего имени информационный шум не увеличивает?) Есть же регионы для группировки
Там около 200 использований функций этого класса в коде, добавили 3 символа, итого просто +600 символов на ровном месте.
У меня есть такое подозрение, что объявление типа кортежа почти всегда будет длиннее, чем имя классаPsyHaSTe
21.07.2017 18:00Если что-то используется больше одного раза (не создание-вызов-получение, а действительно похожие использования), то я бы вопроса такого не ставил — отдельный тип. Чтобы был явный контракт, с отслеживаемыми юзаджами и прочим.
Какой тип вы будете создавать для того, чтобы вернуть две айдишки?
StoreIdPaymentIdPair
? И много он дает выигрыша по сравнению с методом который возвращает(int StoreId, int PaymentId)
?
Вы считаете, что набор полей без общего имени информационный шум не увеличивает?) Есть же регионы для группировки
Набор полей не является самостоятельной сущностью. Это то, чем по определению являются кортежи.
У меня есть такое подозрение, что объявление типа кортежа почти всегда будет длиннее, чем имя класса
Конечно длиннее, в примере выше аж на 8 символов. Ну, правда не надо забывать еще объявление класса строк на 10, но это же такие мелочи.
Подводя итог (уже немного поднадоело писать одно и то же): таплы это отличный способ вернуть несколько значений из метода. Раньше нужно для этого было либо out-параметры воротить, либо именованный класс делать. И плодились просто десятки классов из 2-3 полей, которые используются в одном месте. Если метод публичный — не вопрос, именованный класс должен быть всегда (другой вопрос, зачем публичный метод должен вернуть айдишки, а не объект модели. Так что такой вопрос на практике обычно не стоит). А вот если они приватный, то он абсолютно ничем не хуже анонимного типа. Анонимный тип имеет скоуп метода, таплы — класса.
Free_ze
21.07.2017 18:41-1Какой тип вы будете создавать для того, чтобы вернуть две айдишки? StoreIdPaymentIdPair?
Тип описывается в том числе свойствами, поэтому что-то вроде StorePayment { StoreId; PaymentId }
Набор полей не является самостоятельной сущностью.
Как это? Вы же группируете данные по какому-то признаку. Даже в вашем примере было GetAvailabilityGroupsOn
Конечно длиннее, в примере выше аж на 8 символов. Ну, правда не надо забывать еще объявление класса строк на 10, но это же такие мелочи.
Речь идет о том случае, когда больше одного использования. Но мне не так важны символы, у меня они бесплатные, сколько возможность однозначно установить, что во всех этих местах используется тот же тип, рефакторить, наследовать и прочее.
А вот если они приватный
А если публичный, то в чем отличие?DistortNeo
21.07.2017 19:38+2К сожалению, в C# пока нет механизмов эффективного определения таких классов. Да, можно создать класс
struct StoreIdPaymentIdPair { int StoreId; int PaymentId; }
, но пользоваться им будет неудобно, потому что вместоreturn (a, b);
придётся писатьreturn new StoreIdPaymentIdPair { StoreId = a, PaymentId = b };
.
Мало определить только поля класса, нужно ещё создать конструктор, деконструктор, переопределить
Equals
,GetHashCode
,ToString
. Всё то, что в туплах уже есть. Вы хотите вручную создавать такой класс? Лично я — нет.
Фактически, всё, что нужно — это именованное определение туплов, что-то типа
using StoreIdPaymentIdPair = (int StoreId, int PaymentId)
. И я уверен, что в будущих версиях C# это будет сделано.PsyHaSTe
21.07.2017 23:25И я уверен, что в будущих версиях C# это будет сделано.
Это называется Records, должно было появиться уже в 7 версии, но перенесли.
DistortNeo
22.07.2017 01:09Видимо, не договорились с синтаксисом, и выпустили урезанный вариант Records в виде синтаксического сахара для ValueTuple.
В любом случае, очевидно, что лучше написать
public struct Pair(object First, object Second);
а пока нет Records — просто использовать ValueTuple в виде
(object First, object Second)
,
чем на каждый чих писать простыню кода:
Кодpublic struct Pair : IEquatable<Pair> { public object First { get; } public object Second { get; } public Pair(object First, object Second) { this.First = First; this.Second = Second; } public bool Equals(Pair other) // for IEquatable<Pair> { return Equals(First, other.First) && Equals(Second, other.Second); } public override bool Equals(object other) { return (other as Pair)?.Equals(this) == true; } public override int GetHashCode() { return (First?.GetHashCode()*17 + Second?.GetHashCode()).GetValueOrDefault(); } public Pair With(object First = this.First, object Second = this.Second) => new Pair(First, Second); public static void operator is(Pair self, out object First, out object Second) { First = self.First; Second = self.Second; } }
PsyHaSTe
21.07.2017 23:39А если публичный, то в чем отличие?
В том же, почему приватные поля это ок, а публичные — не очень.
Как это? Вы же группируете данные по какому-то признаку. Даже в вашем примере было GetAvailabilityGroupsOn
Ну необязательно. Другой пример — словарь с ключами по 2 значениям:
private readonly ConcurrentDictionary<(int actionTemplateId, int apiProjectId), EmailMailingProperties> emailMailingPropertiesCache = new ConcurrentDictionary<(int, int), EmailMailingProperties>(); ... var result = emailMailingPropertiesCache[(actionTemplateId, projectId)];
Зачем тут городить явный класс? Тем более что да, нужно реализовывать IEquitable и все прочие прелести.
Заметьте, что во всех примерах все использование ограничено пределами класса и приватными переменными. Все потому, что отдавать наружу
ValueTuple<TItem1, TItem2>
действительно некрасиво. Но для скоупа класса это более чем допустимо.
0xd34df00d
21.07.2017 19:41И много он дает выигрыша по сравнению с методом который возвращает (int StoreId, int PaymentId) ?
Да. Все туплы из одного списка типов являются одним типом, все структуры — разные (даже если они изоморфны), что полезно для типобезопасности.
Набор полей не является самостоятельной сущностью. Это то, чем по определению являются кортежи.
Как только вам надо откуда-то его вернуть или куда-то передать, это становится самостоятельной сущностью.
Вернее, становится-то оно, может, даже раньше, просто в этот момент, на мой взгляд, самостоятельность сущности становится ещё более очевидной.
Подводя итог (уже немного поднадоело писать одно и то же): таплы это отличный способ вернуть несколько значений из метода. Раньше нужно для этого было либо out-параметры воротить, либо именованный класс делать. И плодились просто десятки классов из 2-3 полей, которые используются в одном месте.
В этом нашем C++ с 14-го стандарта можно написать
auto doFoo() { struct { std::string name; std::string id; } result; result.name = ...; return result; }
Это я к тому, что есть альтернативные варианты возвращать список полей и не захламлять пространство имён одноразовыми декларациями.
PsyHaSTe
21.07.2017 23:32Насколько я понимаю, у этого подхода есть проблема с reference assemblies. Суть в том, что в шарпе зачастую референсятся только декларации, а про тело метода ничего неизвестно. То есть грубо говоря у нас есть хедер-файлы, но мы ничего не знаем про содержимое. В таком случае у нас есть
auto doFoo() => throw null;
Пример из стандартной библиотеки. И как вывести отсюда тип — непонятно. И второе, в шарпе вывод типов работает от декларации, то есть нельзя написать.
var x; x = 10;
И вывести тип int. Поэтому в плюсах это работает, а в шарпе — нет.
0xd34df00d
21.07.2017 23:41Суть в том, что в шарпе зачастую референсятся только декларации, а про тело метода ничего неизвестно. То есть грубо говоря у нас есть хедер-файлы, но мы ничего не знаем про содержимое.
Тогда да, в таком виде для анонимных возвращаемых типов это не годится.
то есть нельзя написать… И вывести тип int. Поэтому в плюсах это работает, а в шарпе — нет.
В плюсах так тоже нельзя, в плюсах точно так же оно работает, чай не Хиндли-Милнер (к сожалению). В вышеприведённом коде просто структура анонимная и её тип не имеет названия (такое можно очень давно, чуть ли не с древних чистых сей), а тип выводится не у какой-либо переменной, а у функции, да и не целиком, а всего лишь возвращаемый.
DistortNeo
21.07.2017 15:12Использую все фичи C# 7.0, кроме разве что ref locals/ref returns
Последние я тоже начал использовать. Usecase: передача функции, которая возвращает ссылку на поле объекта. До C# 7.0 приходилось передавать две функции (геттер и сеттер).
khim
19.07.2017 20:08Обычно переход происходит постепенно, по модулям, да.
«По модулям» он происходиттогда, когда у вас нет выбора. Вот посмотрите сюда — где вы тут видите «переход по модулям»? Просто фичи из новых версий языка постепенно разрешаются и людим потихоньку переучиваются. Без руволюций и тотального переписывания всего с нуля.
DistortNeo
19.07.2017 19:57А зачем что-то менять или переписывать? Нужно либо сделать так, чтобы компилятор понимал несколько языков, либо транслировать их в некоторое универсальное представление.
Хорошим примером здесь будет .NET и JVM: вы можете писать модули на разных языках, и они будут совместимы друг с другом.
Бич C++ — отсутствие такого универсального представления. Так, интерфейс модуля распространяется не в виде метаданных, а в виде исходных файлов, что привязывает к конкретному языку.Free_ze
19.07.2017 20:13-2Хорошим примером здесь будет .NET и JVM: вы можете писать модули на разных языках, и они будут совместимы друг с другом.
Однако, вы часто встречали в продакшне проекты под .NET не на C#? Не Android, где цикл поддержки приложения в несколько лет, а махровый интерпрайз. Это идея не взлетела, ибо чтобы поддерживать мультиязычные приложения нужно иметь мультиязычных специалистов или несколько специализированных команд.
И, да, касамо .NET, это выливается в такое уродство, как CLS, на которое ни один здравый архитектор равняться не станет. А история с унылыми дженериками Java также достаточно известна — груз совместимости JVM. Чудес не бывает.PsyHaSTe
19.07.2017 21:27+1Миллион раз видел математическую часть написанную на F#. Иногда видел части на managed C++. На других — не особо, но и этого уже достаточно.
Free_ze
19.07.2017 22:40Я поддерживал проект как с C++/CLI, так и C++ в связке с C#. Докладываю: адок еще тот (= Но это было необходимым злом — железки. В здравом уме языки мешать — так себе затея.
F# лично не встречал еще. И что, серьезные крупные проекты так пишут?PsyHaSTe
19.07.2017 22:58Вполне. Насчет плюсов — согласен, я их до кучи привел :) А вот F# вполне себе, на том же дотнексте много докладов на его тему, а голым его почти никогда не используют. Учитывая совместимость на уровне CIL вполне себе отлично сосуществуют.
mayorovp
20.07.2017 06:34Под .NET есть еще такой язык как Poweshell. И еще можно многие JVM-либы использовать через IKVM.
Free_ze
20.07.2017 08:20При желании можно прикрутить что угодно к чему угодно. Дело в цене такой операции и поддержки после.
PsyHaSTe
20.07.2017 12:04Писал в свое время целую систему под ЕМИАС на powershell. Идея была в том, что если багу найдет админ на проде, он сразу сможет на месте поправить её и таки образом сокращается время обратной связи. В принципе, часто работало, хотя часто знания админов не дотягивали до того, чтобы понять ошибку в логике скрипта.
khim
19.07.2017 19:12-1Слава богу, мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем
Компоненты мы использовать можем, программистов нет. Что бывает, когда языки пытаются менять каждые 10 лет на несовместимые с ними отлично показывает цепочка: Алгол — Паскаль — Модула-2 — Оберон: от языка, вокруг которого, фактически, целая индустрия построена была к просто «популярному» языку, далее к «перспективному» и, наконец, в забвение.
Проблема в том, что каждый раз, когда вы меняете язык несовместимым образом у людей появляется соблазн перейти не на новую версию, а что что-то совсем другое. Скажем Андроид переходит не с Python2 на Python3, а с Python'а на Go…PsyHaSTe
19.07.2017 19:41+2Проблема в том, что каждый раз, когда вы меняете язык несовместимым образом у людей появляется соблазн перейти не на новую версию, а что что-то совсем другое. Скажем Андроид переходит не с Python2 на Python3, а с Python'а на Go…
Мне кажется, что саботировать переход на другой стек это несколько отдает сектантством. Если переходят с питона2 на го, значит го лучше питона3 по каким-то причинам. Что плохого в переходе на го я не вижу, если честно.
khim
19.07.2017 20:06Что плохого в переходе на го я не вижу, если честно.
Плохо то, что приходится менять всю инфраструктуру, все инструменты, переписывать (постепенно) все программы, переучивать людей под новую парадигму и прочее, прочее прочее. Но если у вас язык меняется несовместимым образом — то, собственно, выбора у вас и нет: выбирать «идейного потомка» языка, на котором всё написано большого смысла не имеет.
Мне кажется, что саботировать переход на другой стек это несколько отдает сектантством.
Кто говорит о саботаже? Если бы python2 продолжал развиваться и никто не заставлял бы разработчиков никуда переходить, то он так бы и использовался. Постепенно какие-то фичи бы внедрились, какие-то — «остались бы за кадром». И это было бы всяко лучше «революции» с переходом на python3 или go. Собственно так переход на C++14 произошел.PsyHaSTe
19.07.2017 21:31+1Плохо то, что приходится менять всю инфраструктуру, все инструменты, переписывать (постепенно) все программы, переучивать людей под новую парадигму и прочее, прочее прочее. Но если у вас язык меняется несовместимым образом — то, собственно, выбора у вас и нет: выбирать «идейного потомка» языка, на котором всё написано большого смысла не имеет.
Ну это стоимость перехода. Никто не мешает оставаться на существующем тулчейне и получать эволюционные изменения.
Кто говорит о саботаже? Если бы python2 продолжал развиваться и никто не заставлял бы разработчиков никуда переходить, то он так бы и использовался. Постепенно какие-то фичи бы внедрились, какие-то — «остались бы за кадром». И это было бы всяко лучше «революции» с переходом на python3 или go. Собственно так переход на C++14 произошел.
Так питон2 и продолжает развиваться, разве нет?
Free_ze
19.07.2017 22:43-1Ну это стоимость перехода.
Стоимость мы оплатим реальными деньгами сегодня, а польза-то есть? В обозримом будущем, а не через 10 лет.PsyHaSTe
19.07.2017 22:59Ну это же не я должен считать, а техлид вашего проекта. Посмотреть фичи, посчитать, сколько экономит, посчитать затраты, принять решение. Обычный процесс, ничем не отличается от, например, обычного рефакторинга архитектуры.
khim
19.07.2017 23:36-2Обычный процесс, ничем не отличается от, например, обычного рефакторинга архитектуры.
Отличается. В случае «с обычным рефакторингом» масштаб бедствия ограничен тем компонентом, который рефакторится. В случае с переходом на новый язык — вам приходится переписывать всё. Что не имеет смысла почти никогда.
Варинат: оставить в проекте в долгосрочной перспективе два языка обычно хуже любого из них, так как вам после этого потребуются специалисты знающие их оба. Что, как правило, неприемлемо.
P.S. В большинстве проектов идёт работа с двумя, тремя, а то и с десятью языками — но в разных нишах: никому в голову не придёт идея переписать какой-нибудь компонент с SQL на CSS, к примеру. А тут, раз уж мы берёмся переписывать, то ясно, что это языки «из одной ниши». В этом случае переход может быть осмысленным в том случае если «новый» язык является «продолжением» старого — как в случае с переходом с C на C++ или с C++98 на C++11. Если же этого нет — то, как правило, смысла переходить тоже нет.
Free_ze
20.07.2017 08:27+1Ну мы же спорим с точки зрения техлида. Разработчикам-то понятно: мы любим крутые новинки и рады внедрению новых языков/стандартов)
ведь при плохом раскладе мы всегда можем сменить работуТеоретически, при прочих равных, я с вами согласен — менять языки на более эффективные и удобные нужно.
khim
19.07.2017 23:10Так питон2 и продолжает развиваться, разве нет?
Если вы про Tauthon, то это не совсем то: неофициальная сборка, очень мало пользователей. Хотя чем бог не шутит, может и взлетит. А так — официальная поддержка python2 жёстко прекращена, всех пинками пытаются загнать в рай.
Это дало очень серьёзный толчок развитию Go, так как внушительный процент питонистов просто ушёл на Go, Android — не единственная история.
yarric
20.07.2017 09:35+1Андроид переходит с Python'а на Go? А можно поподробнее?
khim
20.07.2017 17:19Андроид переходит с Python'а на Go?
Пока нет. Переход на Go — рассматривается как один из вариантов. Поддержка своего форка python'а — как другой. Переход на Python3 не рассматривается.
Пока на Go пишутся вспомогательные игрушки типа kati и soongа. По результатам будет видно — будет переход на Go или нет. Причём если kati — это, как описано, просто клон make (на самом деле не совсем), то soong — это уже использование Go как скриптового языка…
khim
19.07.2017 17:55-1Ну насколько я понимаю, RAII говорит про инициализацию ресурса, а не про его дальнейшее использование.
Давайте спорить о вкусе устриц с теми, кто их ел?
ЧитаемПолучение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII)) — программная идиома объектно-ориентированного программирования, смысл которой заключается в том, что с помощью тех или иных программных механизмов получение некоторого ресурса неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта.
Уж одно-то предложение можно было прочитать перед тем, как спорить до хрипоты о том, чего вы не знаете?
RAII — это как раз только, исключительно и обязательно «о дальнейшем использовании» — иначе какой бы в нём был смысл?
Про момент его уничтожения и очистки тут ничего не сказано.
Вы это серьёзно? Получение некоторого ресурса неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта — это, как раз, об очистке.
Собственно вся идиома придумана только и исключительно для того, чтобы правильно удалять обьекты, а не создавать их!PsyHaSTe
19.07.2017 18:24Если бы вы прочитали дальше последнего проицитированного куска, узнали бы, что я сам это понял и написал, что ошибался, и этот вопрос нужно поизучать. Но спасибо за гневный отзыв.
Videoman
18.07.2017 12:59+3Сборка мусора и RAII это ортогональные вещи. RAII намного более универсальная вещь. Сборка мусора решает только одну проблему — освобождение памяти. Но остается еще одна — захват и освобождение ресурсов. Так вот, вторую проблему сборка мусора не решает, так как вы не можете детерминировано (автоматически, не «руками») освободит ресурс пользуясь только ею.
erwins22
16.07.2017 17:14+4C#, Java, Objective D, Go, Rust, Swift — это попытка сделать замену С/С++
Idot
16.07.2017 17:48И кто из них самый быстрый?
alan008
16.07.2017 18:22-1Ну уж точно не C# и не Java. Но кому это сейчас важно?
NeoCode
16.07.2017 18:30-1Самый быстрый тот у которого нативный кодогенератор и развитые средства метапрограмирования. Но вот C# например самый красивый синтаксически. В Go, Rust, Swift есть некоторые идеи, которые при синтаксисе C# были бы вообще конфетки. В D весьма качественно проведена «работа над ошибками» С++, учтены и продуманы очень многие мелочи (это чрезвычайно важно, дьявол как известно именно в мелочах), но к сожалению там больше влияния Java чем остальных языков из списка.
DistortNeo
16.07.2017 23:20Угу, синатксис C# очень приятный и лаконичный. Например, лямбда для возведения в квадрат в C# выглядит как `x => x * x`, тогда как в C++: `[](auto x) { return x * x; }`.
Просто C++ дорабатывается инкрементально, допиливанием нового функционала поверх старого, причём так, чтобы ничего из старого не поломалось, вот и получается многословность и куча неудобств.
В C++ же мне очень не хватает двух вещей, которые есть в C#: concepts и extension method.
Первое приходится имитировать через CRTP, что немного неудобно, второе — через объекты-адаптеры (пример).FoxCanFly
17.07.2017 17:22И зачем в C++ extension method нужны? Это просто синтаксический сахар, который для консистентности в чисто ОО языках. C++ мультипарадигменный и имеет свободные функции
DistortNeo
17.07.2017 21:12+1Вы в своём же комментарии ответили на свой вопрос. Это просто удобный синтаксический сахар. Почему можно где угодно перегружать операторы, а «добавлять» к классу методы — нет?
В C++ можно обойтись без auto, можно обойтись без лямбд, явно реализуя классы-функторы и т.д., но с сахаром-то лучше.FoxCanFly
18.07.2017 13:05+2Наверное, потому что это делает ту же работу, что и переопределение свободных функций (см стандартные std::swap, std;:begin, std::end)? Зачем делать дублирующиеся возможности в языке? Просто что бы было «как в C#'?
DistortNeo
18.07.2017 14:32+1> Зачем делать дублирующиеся возможности в языке?
Затем же, зачем в С++ дублируются и другие возможности, о которых я писал выше. Вызов конструктора с круглыми или фигурными скобками, auto и decltype, лямбды и объекты-функторы — это всё сахар. Единственное нововведение после С++03, не являющееся сахаром — это move semantics.
Я просто хочу писать `v.sort()` вместо `std::sort(v.begin(), v.end())`.mayorovp
18.07.2017 15:23+2Тогда уж
v.std::sort()
получится. И я не вижу чем это лучше гипотетическогоstd::sort(v)
TheShock
18.07.2017 20:54+1ну когда один аргумент, то вроде без разницы. Но, как на меня, первое смотрится хуже чем второе:
if between(foo, min, max) { // do something }
if foo.between(min, max) { // do something }
0xd34df00d
19.07.2017 19:24Надо просто синтаксис для инфиксной формы записи функций, что-то вроде
if foo `between` min max
(в очередной раз валидный хаскель, кстати, да).
DistortNeo
18.07.2017 21:18Вы, видимо, не особо связывались с C#, раз не очень меня понимаете.
Fluent interface для любой коллекции (класса с интерфейсом `IEnumerable`) — чертовски удобная вещь.
Если вам надо сделать несколько действий над коллекцией, например выборку по условию, затем преобразование, затем выбор первого результата, то в C# будет выглядеть так:
var x = v.Where(...).Select(...).First();
Ничего лишнего, просто и понятно.
В C++ из коробки же такого функционала нет. Сначала вызывать `copy_if`, создавая новую коллекцию, затем `transform`, снова создавая коллекцию. Либо, вручную писать цикл `for` по элементам коллекции, что не очень приятно.
Либо вообще писать так:
first(select(filter(v, ...), ...), ...)
Видимо, я действительно очень хочу uniform call syntax.mayorovp
18.07.2017 22:16В C# расширения используются во многом потому что у интерфейсов не было методов с реализацией. Но в С++ нет интерфейсов...
yarric
20.07.2017 01:13Не совсем понятно, что происходит с памятью в красивой строчке типа
var x = v.Where(...).Select(...).First();
. Создаётся (а потом уничтожается) несколько временных объектов? Как написать такое же на C++, что будет у такой реализации под капотом?DistortNeo
20.07.2017 01:27Не совсем понятно, что происходит с памятью в красивой строчке типа
var x = v.Where(...).Select(...).First();
Используются ленивые вычисления, временные коллекции не создаются, за редким исключение
Как написать такое же на C++, что будет у такой реализации под капотом?
Мне, правда, ни то, ни другое не нравится. Это же C++, почему бы не использовать статический полиморфизм?
PsyHaSTe
20.07.2017 12:09+2LINQ может работать не только с памятью, но и с БД, В последнем случае количество созданных объектов будет ровно 1 — тот, который вернет метод First. Остальное сконвертируется в запрос к БД.
Если мы итерируемся в памяти, то каждый вызов будет создавать один легковесный объект итератора, который по сути имеет одну ссылку на перечисляемую коллекцию (или предыдущий итератор), и умеет его как-то преобразовывать. В итоге количество созданных объектов — 1 + количество легковесных итераторов. Но на них обычно всем пофиг, тем более, что это обычно структуры, которые еще и автоматически разрушаются при выходе из скоупа.
Ну и напоследок, я все мечтаю написать свой препроцессор, который все это дело будет разворачивать. В принципе, такой уже есть, как stand-alone обертка над компилятором, но хотелось бы что-то встроенное в IDE.
DistortNeo
20.07.2017 16:08+1LINQ может работать не только с памятью, но и с БД
Есть маленький нюанс: в этом случае лямбды, которые передаются в качестве параметров методам
Select
,Where
, не компилируются в код, а представляются в виде Expression tree, т.е. нужна поддержка со стороны компилятора. Без этой поддержки Expression Tree пришлось бы строить вручную, и это было бы менее удобно, чем написание SQL-запросов руками.
Ну и напоследок, я все мечтаю написать свой препроцессор, который все это дело будет разворачивать. В принципе, такой уже есть, как stand-alone обертка над компилятором, но хотелось бы что-то встроенное в IDE
А с какой целью? Ради увеличения производительности? А вы уверены, что производительность упирается именно в LINQ-запросы?
В C++ этого эффекта добиться легко (статический полиморфизм), в C# без кодогенерации же не обойтись, т.к. инлайнить лямбды он не умеет.
PsyHaSTe
21.07.2017 12:16-3Есть маленький нюанс: в этом случае лямбды, которые передаются в качестве параметров методам Select, Where, не компилируются в код, а представляются в виде Expression tree, т.е. нужна поддержка со стороны компилятора. Без этой поддержки Expression Tree пришлось бы строить вручную, и это было бы менее удобно, чем написание SQL-запросов руками.
Компилятор тут не при чем. Можно свой провайдер написать, который вместо скирпта SQL генерирует стихи. Компилятор про LINQ ничего не знает, и наоборот, добавление информации о LINQ в компилятор нежелательно:
Currently all of the LINQ keywords do explicitly translate to methods following the LINQ convention. It doesn't even care if they are instance methods or extension methods. This enables designing LINQ capable APIs that aren't enumerable, or even compile-time specialization of LINQ implementations.
In fact, having the compiler explicitly rewrite LINQ keywords could break existing code. I can currently write extension methods to specialize LINQ implementations and the compiler will prefer that implementation over the one provided by the BCL.
The C# compiler knows nothing about where any type/method comes from. And I don't think it should.Это что касается кейвордов. А методы как были методами, так и остаются. Вопрос, какая такая поддержка компилятора еще нужна LINQ?
А с какой целью? Ради увеличения производительности? А вы уверены, что производительность упирается именно в LINQ-запросы?
mayorovp
21.07.2017 12:29Компилятор тут не при чем. Можно свой провайдер написать, который вместо скирпта SQL генерирует стихи. Компилятор про LINQ ничего не знает.
Зато компилятор знает про Expression tree, которых в C++ нету.
PsyHaSTe
21.07.2017 12:45-3Компилятор знает про один из видов записи экспрешнов в виде лямбды. Никто не мешает вручную собрать его с помощью фабрики класса
Expression
. Поддержка со стороны компилятора облегчает запись, но не необходима.DistortNeo
21.07.2017 15:30+2Компилятор знает про один из видов записи экспрешнов в виде лямбды. Никто не мешает вручную собрать его с помощью фабрики класса Expression.
Точно так же никто не заставляет вас использовать LINQ. Вы можете вручную конструировать SQL-запросы. Вы можете вручную разворачивать LINQ-код для повышения производительности.
Поддержка со стороны компилятора облегчает запись, но не необходима.
Неужели вы не понимаете, что без этой поддержки компилятором ни один нормальный человек бы не стал бы тогда использовать LINQ?
PsyHaSTe
21.07.2017 15:38+1Хороший поинт, пожалуй соглашусь.
Правда, уже несколько лет как можно самому сделать такую поддержку как плагин для компилятора (а-ля LLVM мидлварь). Так что с одной стороны я согласен, что поддержка нужна. С другой стороны, практически любой программист может сам её завести, написав за пару дней соответствующий плагин или взяв уже существующий.
Вы можете вручную разворачивать LINQ-код для повышения производительности.
Могу. Но почему-бы этим не заняться компилятору? У него вся информация на руках, остается только ей воспользоваться.
0xd34df00d
21.07.2017 19:46Зато компилятор знает про Expression tree, которых в C++ нету.
Expression tree на плюсах делается довольно просто.
Я тут развлечения ради как-то недоORM наваял, там есть вещи вроде
struct Record { std::string name; int id; double value; }; Adapted<Record> adapted; adapted.Select(_1 == "foo" && _3 >= 3.14159);
с типобезопасностью и поэтессами. Жаль, не придумал, как с минимальным шумом ссылаться на поля по имени, а не по индексу.
devalone
21.07.2017 21:03Как раз недавно нужна была ORM, но я у меня моделей в базе немного, поэтому не найдя ничего подходящего решил писать sql запросы вручную.
Можно ссылочку на гитхаб?0xd34df00d
21.07.2017 23:42Оно в рамках другого проекта, недопилено, почти ничего не умеет и вообще, но, тем не менее,
https://github.com/0xd34df00d/leechcraft/blob/master/src/util/db/oraltypes.h?ts=4
https://github.com/0xd34df00d/leechcraft/blob/master/src/util/db/oral.h?ts=4
Надо бы довести до ума и в библиотеку вынести.
Antervis
18.07.2017 19:25в с++20 вероятно появятся Ranges. Одна из «фич» этого нововведения как раз состоит в том, что все методы, принимающие пару итераторов, смогут принимать один Range-объект. Значит, будут валидны и sort(v);, и vector v = list{1,2,3}; и прочее
Antervis
18.07.2017 19:34+1п.с. кстати, есть же еще propolsal по uniform call syntax. Чтобы можно было писать std::sort(v) и v.sort(), вызывая по факту один метод
a-tk
17.07.2017 21:57+1В D к примеру есть UFCS (Unified Function Call Syntax), который считает эквивалентными код
F(x, a, b, c) и x.F(a, b, c). И есть чёткие правила поиска. Притом работает для любой свободной функции как минимум с одним аргументом.
В C# приходится лапками больше поработать, чтобы сделать такое. C++ половину пути в одну сторону преодолел в виде специализаций шаблонов типа std::swap, а вот в другую сторону путь даже не начинал.mapron
18.07.2017 17:50ну как не начинал, драфты уже давно появились:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0131r0.pdf
GamePad64
18.07.2017 21:21Были же предложения и Саттера и Страуструпа. Возможно, к C++20 сделают, хотя надежды всё меньше.
NeoCode
19.07.2017 12:49И зачем в C++ extension method нужны? Это просто синтаксический сахар, который для консистентности в чисто ОО языках. C++ мультипарадигменный и имеет свободные функции
А зачем тогда С++ нужен, если есть ассемблер? А функции это синтаксический сахар для инструкции CALL.
Antervis
19.07.2017 15:03-2И зачем в C++ extension method нужны?
чтобы иметь возможность переопределять поведение свободных функций для собственных классов.
Например: у std::list есть метод sort(), однако std::sort для std::list неприменим, т.к. std::list::iterator не удовлетворяет требованиям RandomAccessIterator. С UCS std::sort(list) будет эквивалентен list.sort();
a-tk
17.07.2017 21:48В C# тоже в общем-то. Помнится, был жуткий вой среди разработчиков при введении нового ключевого слова var.
После этого но одно новое средство языка не ломало обратную совместимость в том смысле, что новый компилятор всегда компилировал код, написанный под более старую версию компилятора. Отсюда растут некоторые странные конструкции, такие как pattern matching например.
Idot
16.07.2017 18:36+8кому это сейчас важно?
Очень Важно, потому что C++ применяется там, где важна именно скорость. А там где скорость не важна — возможно использовать любой другой язык.
=> язык претендующий на то чтобы заменить собой C++, должен быть достаточно быстрым.khim
16.07.2017 20:52На самом деле не только скорость. На нём ещё микроконтроллеры программируют где память в сотнях байт (даже не в килобайтах!) меряется. Так что из всего этого разнообразия только D и Rust могут на полноценную замену C++ претендовать… И только только в перспективе…
Antervis
17.07.2017 05:54+2нет у них перспективы, они на слуху из-за одного лишь хайпа. С++ развивается быстрее того же D, а Rust по синтаксису не лучше плюсов
PsyHaSTe
19.07.2017 15:25Ну я пробовал — как по мне намного лучше. Нормальные дженерики (а не темплейты), приятный синтаксис паттерн матчинга, not-null по дефолту и безопасная работа с памятью. Все это на голову выше просто уровня плюсов.
alexeykuzmin0
19.07.2017 16:20Плюсы тоже бывают разные. Если следовать Core C++ Guidelines, то жизнь резко становится гораздо проще
yarric
20.07.2017 01:16А в C++ чем работа с памятью небезопасная? Не используйте
new
иdelete
, и будет вам счастье.DistortNeo
20.07.2017 01:29А ещё забудьте о
reinterpret_cast
и постарайтесь обойтись без указателей.devalone
20.07.2017 16:18+1При нормальной архитектуре он и не нужен, а если вы пишите
или(Type1)valOfType2
, компилятор считает, что вы знаете, что делаете и не получите в итоге UB.reinterpret_cast<Type1>(valOfType2)
А обычные указатели не так уж и часто нужны(если не писать на Qt, но там сборка мусора немного своеобразная), для массивов есть контейнер std::vector, для всего остального умные указатели std::unique_ptr, std::shared_ptr, std::weak_ptr, с которыми проблем не возникает.0xd34df00d
20.07.2017 23:42Проблема с умными указателями возникает тогда, когда могут возникать циклы (правда, тогда и borrow checker'ам всяким не всегда прям так уж хорошо). Или когда вам нужно при создании агрегированного объекта отдать умный указатель на себя же (и начинаются пляски с
enable_shared_from_this
, двухстадийной инициализацией и прочими прелестями).devalone
21.07.2017 19:03Вы про циклические зависимости? Для решения этих проблем есть std::weak_ptr
0xd34df00d
21.07.2017 19:48Ну, начнём с того, что weak_ptr не всегда передаёт семантику владения. Если оба объекта действительно владеют друг другом, то неочевидно, какой из них должен иметь weak_ptr, а какой — обычный shared_ptr.
0xd34df00d
20.07.2017 23:40А ещё не стучитесь до одного объекта из-под нескольких тредов. Или стучитесь, но только константные методы дёргайте, и надейтесь, что всё мутабельное внутри прикрыто мьютексами.
Antervis
21.07.2017 04:42недавно была статья об умном указателе на с++, который при разыменовании захватывает мьютекс.
0xd34df00d
21.07.2017 07:04Проблема в том, что это всё делается руками и держится на честном слове разработчика.
Antervis
21.07.2017 09:14+1Нет. Оператор -> оборачивающего указателя переопределен и возвращает прокси-объект, через RAII захватывающий и освобождающий мьютекс. Соответственно, при вызове ptr->someMethod(); мьютекс будет захвачен в течение всего времени выполнения someMethod()
0xd34df00d
21.07.2017 19:48Вам компилятор это всё предоставляет? Нет, это должен сделать программист, пусть и один раз в полубиблиотечном коде.
yarric
22.07.2017 16:04Компилятор тоже делает программист. В том же Rust как-то уже находили ошибки в borrow checker-е.
0xd34df00d
24.07.2017 19:55+1Доступная площадь для совершения ошибки при этом поменьше.
yarric
24.07.2017 21:14-1Зато гибкости гораздо больше. А все эти unsafe, позволяющие реализовывать подобное в Rust, выглядят как костыль.
0xd34df00d
24.07.2017 23:56+2Так это и есть костыль, связанный с недостаточной выразительностью системы типов. Но я бы предпочёл, чтобы unsafe и соответствующая гибкость была в очень изолированных местах, которые можно было бы особенно дотошно отревьюировать, отдебажить и так далее.
yarric
16.07.2017 23:35+2Но кому это сейчас важно
На фоне приближения к пределу размеров транзисторов — ещё как важно.
devalone
16.07.2017 20:45+82D Graphics
И зачем это в стандартной либе?erwins22
23.07.2017 09:05лет 20 назад я бы тут бесновался — «как вы не понимаете?» а сейчас интерфейсы редко пишут на с++ потому что 20 лет назад не было 2Д
khim
23.07.2017 11:01Есть у меня подозрение, что тут не в стандартной библиотеке дело. Интерфейсы сейчас частенько пишут на Java, к примеру… совершенно при этом не используя тот 2D, который в стандартной библиотеке Java!
devalone
23.07.2017 19:06+1Интерфейсы пишут на специальных фреймворках вроде Qt, которые облегчают их создание или на специальных языках описания интерфейсов типа QML, потому что это удобнее. А в стандартной либе 2D не место, пускай лучше тратят силы на создание того, что нужно большинству.
yarric
23.07.2017 19:39-1А на чём тогда пишут интерфейсы всех эти Photoshop, Word, AutoCAD, Chrome и т. д.?
dendron
23.07.2017 12:43Пора уже переименовать комитет в Boost Standartization Committee, а язык — в Boost++.
khim
Интересно, почему «явные обобщённые лямбда-функции» — это серьёзно, а совместимость с C99 (на том же уровне, чистый синтаксический «сахар» — но весьма и весьма удобный, позволяющий, в частности, вызывать функции «почти что с именованными параметрами») — нет…
0xd34df00d
Потому что теперь можно нормально делать SFINAE на лямбдах!
mapron
Как мы без этого жили.
0xd34df00d
Да и без C++11 жили всего-то еще 7 лет назад.
mmatrosov
Что будет совершенно не нужно при появлении концептов :)
0xd34df00d
Но уметь адекватно называть тип, не прибегая к вещам вроде
[](auto x) -> requires Sortable<decltype(x)> && Orderable<decltype(x)>
(или как это вообще писать-то?), будет тоже полезно.Antervis
Тогда уж
Хотя Sortable — это подмножество Orderable и тогда requires не нужен
0xd34df00d
Несимметрично как-то.
Верное замечание! Но суть примера не в этом.
Antervis
Да там много «но». Например, концепты появятся в стандарте вместе с explicit generic lambdas, и можно будет писать так:
Плюс, уже было подмечено:
Ограничивать лямбды концептами как правило незачем. Они всё равно используются локально. А SFINAE делается и без лямбд
0xd34df00d
Что лучше, чем если вместо
T
придётся писатьdecltype(x)
, попутно вспоминая, в чём различия между выводом типа через decltype и обычное определение параметра шаблона.Я согласен, что это нужно очень редко. Единственный случай, пожалуй, когда лично мне это нужно — когда пишутся всякие визиторы через наследование от набора лямбд.
mmatrosov
Обсуждался синтаксис
[](Sortable x) {}
. Если условие составное, как у вас, то можно создать новый концепт. Но в целом да, это адекватный пример.