Pull to refresh

Comments 59

>> Теперь выглядит лучше.
Блин, он всё страшнее и страшнее с каждой версией :))

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

вспоминаю себя новичком: сложнее всего мне было понимать виртуальные функции и битовую магию (по сей день её недолюбливаю). Вывод: новичку что новый код, что старый сложно читать. Просто новый в среднем короче раза в полтора-два (из личного опыта).

"короче" не значит "лучше". Последнее время много приходится работать с Go. Там совсем не короче все. Зато даже бегло зная язык прям все понятно, даже не вчитываясь. С "новым" С++ такого не достичь, даже с большим опытом работы с языком. Особенно если в команду затесываются любители эксперементировать. Есть опыт когда код просто стал неподдерживаемым. И человек который его написал сам решил что переписать легче чем править и дополнять.

Один любитель поэкспериментировать налажал значит язык плохой? Важно же соотношение: одна нетривиальная строка явно хуже одной простой и явно лучше 10 простых. Можно в качестве альтернативы с++ взять старый добрый тривиально понятный чистый си. Сколько строк кода займет написание гетерогенного контейнера на си?

Лучше не так.
Сколько строк займет написание простой и понятной альтернативы этому коду?


template < typename... Func > class Visitor : Func... { using Func::operator()...; }
template < class... Func > make_visitor(Func...) -> Visitor < Func... >;
На си? Могу бегло предположить, что несколько сотен. Надо же будет эмулировать перегрузки функций. На старых плюсах без variadic templates? Variadic macros выручат конечно, но вот аналог using Func::operator()...; придется делать через публичное наследование

п.с. есть проползал на std::overload, как раз для этих целей.
С каждой версией С++ приближается к чему-то хтоническому…
Так думают либо те, кто далек от с++, либо те, кто остановился в профессиональном росте лет 5-10 назад
Так говорят только те, кто не видит и не пишет ни на чём, кроме с++…
Ну да ;) Утверждение-то всё равно верно.
+1… С++ мертв. Есть куча языков на порядок проще, элегантее и главное на которых пишут чтобы получить результат, а не обсуждать конструкции…
То, что я говорю о хтоническом виде конструкций в новом стандарте не значит, что мне не нравится язык, или я считаю его мёртвым. или я далёк от с++, или я остановился в профессиональном росте, или не собираюсь в этом разбираться.

Это значит, что в новом стандарте конструкции выглядят хтонически.

Например каких языков?

А ну, альтернативы?


Что там в 2017, уже все пишут десктопные приложения на Go, или, может, Mozilla переехала на Servo, написанный на Rust? Есть монструозная Java, есть привязанные к своим платформам C# и Swift. Причём, первые два ещё и требуют докачивать среду выполнения и устанавливать её.


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

Многим веб-разработчикам тоже кажется, что веб работает на java, c#, python, php и далее по списку.
А на деле оказывается, что все БД (PostgreSQL, MySQL, SQLite, MongoDB, Redis, etc) написаны на C/C++, все популярные серверы написаны на C/C++ (nginx, apache, lighttpd, etc), все популярные браузеры написаны на C/C++ (лиса, хром, опера, etc), многие веб-движки и фреймворки написаны на C/C++ (memcached, V8, NodeJS, etc), многие частоиспользуемые нетривиальные сервисы написаны на C/C++ (инфраструктура Google, Яндекса, Facebook, etc).

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

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

Что вы понимаете под словом «хтонический»? Никак не могу понять, что вы имеете ввиду.
Магически, не очевидно, страшно, запутанно, и при этом мощно, функционально, с богатыми возможностями для применения. Проще говоря, есть в этом что-то от первобытной магии.
Тогда согласен с вами :)
UFO just landed and posted this here

Мне вообще трудно понять, для кого Rust сделан: любители знаков препинаний плотно сидят на C++, любители Python не оценят.

Вот эта строчка должна напугать? Можно было бы что-то "пострашнее" найти.

Нужно различать фичи, которые добавлены для авторов библиотек от фич для прикладных программистов. Например благодаря auto на C++ стало поудобнее писать, чем на Java/С#.

Я конечно далек от С#, но разве их var не что-то похожее на auto?
Вопрос по reduce: а нельзя было использовать std::bind вместо лямбды?

На первый взгляд — как будто можно. Но там могут возникнуть вопросы с сигнатурой сгенерированного функтора.
В общем нужно попробовать на досуге :)

Текст этой ошибки не удастся разобрать, даже если вы используете очень простой код

чтобы избежать таких вот проблем, необходимо проверять входные значения через std::enable_if. А это ОЧЕНЬ много кода

Да, поэтому я и упомянул Concepts TS, который можно потрогать в последних gcc. Ну и какую — никакую реализацию можно глянуть в репозитории, ссылка на который в конце статьи

Жаль что они не попали в с++17, там бы и ranges могли подтянуться.

Кстати, а в каких заголовочных файлах определены library concepts?

Насколько я понимаю, пока нигде. В репозитории gcc на гитхабе концепты есть только в тестовых файлах

vector shapes;
shapes.emplace_back(EquilateralTriangle { 5.6 });
shapes.emplace_back(Square { 8.2 });

Не понял. vector — это std::vector? Это же не скомпилируется. Где тут variant?
using Shape = variant<Circle, Square, EquilateralTriangle>;

vector<Shape> shapes;
shapes.emplace_back(EquilateralTriangle { 5.6 });
shapes.emplace_back(Square { 8.2 });

В статье, в принципе, пошли по пути создания собственного контейнера-адаптера с запоминанием складываемых типов :). От того же класса — визитора для последующей работы с ним никуда не ушли)


Здесь же лёгкие функции для более удобной работы с std::variant, который упрятан в любой неассоциативный контейнер.

Мой комментарий был к предложению в статье, о том что полноценные гетерогенные контейнеры в C++ сделать нельзя. Можно! Правда реализация и недостатки не позволяют использовать это в production.

Что касается visitor, по-моему, по другому обойти гетерогенный контейнер не получится, так что в любой реализации будет visitor

Эмм,… А где написано, что нельзя?)

Ну дык я и говорю, что можно :)

p.s. Вы правы, ваша формулировка допускает, что они могут быть :)

Пишу на С++ с 90х, и с каждой версией все больше и больше нравится простой С, а сейчас — Go. Потому что там нельзя написать что-то вроде


template < typename... Func > class Visitor : Func... { using Func::operator()...; }
template < class... Func > make_visitor(Func...) -> Visitor < Func... >;

Потому что это write-only код. Его нельзя пробежать глазами и сразу понять что он делает. Даже если его сам написал месяц назад. Единственный плюс такого кода — почесать себе эго тем что красиво что-то написал и сэкономил десяток строк. Код должен быть для людей. Компилятору пофиг что компилировать ведь.

P.S. несмотря на мои кряхтения, статья хорошо описывает std::variant и с чем его кушать. Спасибо.

У меня были такие же мысли, когда было мало практики с variadic packs. Нужно побольше попрактиковаться, понять концепции, которые двигали авторов при создании такого синтаксиса и всё становится намного легче, в частности и с вышеприведенным кодом :)


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

Это окупается удобством использования библиотек в прикладных программах.


Go какбы ни разу не альтернатива С++ из-за сборщика мусора и вот этого всего.

В начале мелкое замечание не по сути статьи: заменой «устаревшего» new в данном случае очевидно должна служить функция make_unique, а не make_shared, как в вашем тексте. Хорошо ещё что в данной статье нет сравнительного теста производительности, иначе данная мелкая оплошность могла бы квалифицироваться уже как подделка под нужные результаты.

Далее, по самой статье. Вы как-то быстро перешли к мелким техническим деталям, совершенно не пояснив зачем это вообще нужно. Т.е. из вашей статьи совсем не очевидно когда надо применять полиморфизм на виртуальных функциях, а когда указанный подход. Лично я начал бы подобную статью с перечисления возможных вариантов реализации подобных динамических гетерогенных коллекций (например виртуальные функции, variant, any) и обсуждениях их плюсов и минусов. Типа такого:
— виртуальные функции: реализуются через ссылочное размещение (минус — ухудшение производительности из-за лишней косвенности), требуют правки хранимых типов данных в виде добавления общего наследника (минус — нельзя сделать коллекцию из int, double, string), не ограничивают число хранимых типов (плюс — можно бесконечно расширяться за счёт новых классов-потомков).
— variant: реализуются через размещение по месту (плюс — нет ухудшение производительности), не требуют правки хранимых типов данных (плюс — можно сделать коллекцию из int, double, string), ограничивают число хранимых типов (минус — нельзя добавить новые хранимые типы без модификации кода контейнера).
— any: реализуются через ссылочное размещение (минус — ухудшение производительности из-за лишней косвенности), не требуют правки хранимых типов данных (плюс — можно сделать коллекцию из int, double, string), не ограничивают число хранимых типов (плюс — можно расширяться на любые хранимые типы).

А т.к. в вашей статье в качестве примера приводится как раз случай реализуемый с помощью виртуальных функций (в контейнере нет например базовых типов) и нет тестов производительности, то из неё совершенно непонятно в чём вообще смысл применения подобных решений. Единственная попытка сравнения свелась к весьма субъективному понятию красоты кода, причём в данном конкретном случае ещё и не вполне однозначному (для данного конкретного случая вариант на полиморфных функциях будет покрасивее, если записать всё корректно).

Ну а в остальном статья конечно же очень хорошая и чувствуется, что автор хорошо знаком с особенностями метапрограммирования на шаблонах.

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

Ну как бы variant и any — это совсем не игрушки, а вполне фундаментальные вещи, введённые в стандарт. Собственно это C++ аналоги union и void* из языка C. И важно понимать преимущества и недостатки их применения.

Более того, если мы взглянем на библиотечку TypeErasure из Boost'а (т.е. опять же очевидно, что уже не игрушка, а вполне используемое на практике), то увидим, что в ней центральную роль занимает слегка расширенная версия any, позволяющая реализовать все указанные вами в статье примеры приблизительно в одну строчку. )))

variant и any — не игрушки)
А то, что написал я — пока игрушки:)
По крайней мере до тех пор, пока не подойду к вопросу более фундаментально)

Объясните пожалуйста что означает этот код:


template < typename... Func >
class Visitor : Func... { using Func::operator()...; }
template < class... Func > make_visitor(Func...) -> Visitor < Func... >;

После объявления класса не стоит ;, поэтому это не скомпилируется. Почему, при объявлении параметров шаблона в одном месте использован typename, а в другом — class? И самое главное — что означает последняя строка? Выглядит как User-defined deduction guides, но не компилируется из-за неправильного синтаксиса.

автор просто сказал «реализуйте что-то вроде этого:». Настоящий код (упрощенная версия, без perfect forwarding'а и корректного noexceptness) выглядел бы так:
template <typename ...Func>
struct Visitor : Func... {
    Visitor(Func ...f) : Func(f)... {}
    using Func::operator()...;
};

template <typename ...Func>
auto make_visitor(Func...f) {
    return Visitor<Func...>(f...);
}


Пример использования. Тут функторы должны быть copy-coonstructible
UFO just landed and posted this here

А в языке с отсутствующей типизацией есть деление на гомо- и гетерогенные?

Спасибо за статью, очень интересно. P.s. В примерах у вас опечатка "[]](Circle& c) { c.Print(); ", лишняя ] скобочка.
о! а я, читая вчера статью с телефона, думаю: не забыть потом загуглить, что за новый вид захвата переменных в лямбды придумали, и почему такой вычурный синтаксис?)
Если код понятен менее 90% среднестатистических программистов — нафиг такой код. Код пишет один программист, но поддерживать его приходится годами другим программистам.
UFO just landed and posted this here
Only those users with full accounts are able to leave comments. Log in, please.