Pull to refresh

Comments 14

Обобщённое программирование — ключевое преимущество C++. Я знаю не все языки, но ничего подобного и на таком уровне не видел.

Если вы про именно обобщённое программирование, то какой-нибудь Haskell уделывает С++ и по удобству, и по продуктивности. И появление концептов пусть и улучшит ситуацию, но ненамного. Как минимум потому что они необязательные.


Если же вы про метапрограммирование, то примеров гораздо лучших решений хватает. Был такой язык Nemerle. Который позволял полноценное метапрограммирование с использованием квазицитат и паттерн-матчинга по частям AST. Ещё из недавно виденного — Zig и его comptime, который позволяет, к примеру, генерить обобщённые типы в отсутствии дженериков. Тот же Template Haskell, если не ошибаюсь.


Вообще же, шаблоны С++ как инструмент обобщённого или метапрограммирования далеко не предел. И далеко не идеал.

Спасибо! Да, стоило добавить, что имеются ввиду императивные строго типизированные языки и именно обобщенное, а не метапрограммирование. В функциональном мире слишком много всего интересного.

Что касается второго, то в кругах C++ активно обсуждается введение рефлексии.

Да, то что я пишу, что я другого такого языка не знаю, не значит, что его нет. Я знаю далеко не все языки)

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

Потому что последний шаблонный параметр может быть variadic.

Зачёт! Но есть ещё одна причина.

ну или необязательный:


template<class T, class X, class Y=DefaultY> concept Foo = .....;

... Foo<TheX> ...
... Foo<TheX, CustomY> ...
Однако у обобщённого программирования в C++ есть огромный минус: возникающие ошибки — это боль.

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

Плюсовый компилятор, если не вдаваться в подробности, состоит из собственно компилятора C++ (без шаблонов) и интерпретатора мета-языка шаблонов. И если с отладкой исполняемого кода всё более или менее в порядке, то отлдка мета-языка находится где-то на уровне 60-х годов прошлого века: на входе колода перфокарт, на выходе телетайп. Как-то посмотреть промежуточные результаты вычислений интерпретатора весьма затруднительно. Не говоря уже про точки останова и прочие плюшки отладчика.

Конечно отладка — это не вопрос Стандарта. Но и производители компиляторов и IDE не спешат что-то делать в этом направлении. (А прошло, на минуточку, уже больше 30 лет с момента появления первого компилятора C++.) Поэтому метапрограммирования на C++ стараются, по возможности, избегать (иногда даже запрещать на уровне корпоративного code-style). А когда это невозможно, метапрограммные части кода являют собой достаточно примитивные конструкции, правильность которых можно оценить невооружённым глазом. И только малый процент мета-кода является действительно сложными программами на мета-языке.

Надо ещё сказать, что форк Apple llvm, несмотря на версию компилятора clang 12.0, поддерживает концепты на уровне синтаксиса (ещё бы, компилятор-то взяли свежий), но не стандартной библиотеки!
Чем это объяснить, кроме ударенности эппловцев, я не знаю.

По поводу эпитета. Не хватает промежуточного между "супер" и "так себе".


С одной стороны, фича крутая. А с другой, она уже принесла горюшко: дефект в ABI. Авторы всех известных компиляторов забыли/забили декорировать ограничения в именах функций.
И я их, в принципе, понимаю. Одно дело, когда у тебя есть кортеж типов — аргументов функции, плюс кортеж параметров шаблона.
И другое — когда у тебя синтаксическое дерево ограничений! Которое надо сравнивать с точностью до несущественных имён, а то и до перестановок. А может быть, и до ассоциативности дизъюнкций-конъюнкций…
И вот это вот всё добро нужно компактно закодировать в строку.


Представим себе, что у нас (в нескольких единицах трансляции) есть вот такое:


template<class T> concept Foo = alfa<T> && beta<T>;
template<Foo T> void f();

template<class T> concept Bar = alfa<T> && beta<T>;
template<class T> void f() requires Bar<T>;

template<class T> void f() requires alfa<T> && beta<T>;

Здесь одна и та же перегрузка f() объявлена, или разные?


А вот такое:


template<class T> void f() requires true;
template<class T> void f() requires true && true;

Ну или самое вот такое:


template<class T> void f() requires(T t, T u, T v) { foo(t, u, v); };
template<class U> void f() requires(U t, U u, U v) { foo(u, v, t); };

Оговорка про разные единицы трансляции — к тому, что инстанцирование функции из шаблона не приведёт к неоднозначности.
Но должно или не должно приводить к нарушению ODR, с одной стороны, и к формированию единого адреса функции, с другой стороны?


Как вариант: сперва напишем объявление шаблона, а ниже — эквивалентное определение этого шаблона. Эквивалентное с точностью до чего?

Давно пора добавить в С++ Рефлекшин, а то он в каждом компиляторе есть, но компилятор его вам не выдает. Это автоматически сделает легким автоматическую сборку мусора без которой уже нельзя. Управляемая память (как в C#) в таком случае становится просто синтаксическим сахаром.
> Я знаю всего один, в котором есть что-то похожее, — Haskell.

А какие вы видите принципиальные несхождения с traits в Rust? Или просто не смотрели на него?
Sign up to leave a comment.