Как стать автором
Обновить

Комментарии 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++ и теперь выглядят ненамного проще.

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

Мне вообще трудно понять, для кого 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
НЛО прилетело и опубликовало эту надпись здесь

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

Спасибо за статью, очень интересно. P.s. В примерах у вас опечатка "[]](Circle& c) { c.Print(); ", лишняя ] скобочка.

Спасибо, поправил)

о! а я, читая вчера статью с телефона, думаю: не забыть потом загуглить, что за новый вид захвата переменных в лямбды придумали, и почему такой вычурный синтаксис?)
Если код понятен менее 90% среднестатистических программистов — нафиг такой код. Код пишет один программист, но поддерживать его приходится годами другим программистам.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории