avva: (Default)
[personal profile] avva
(для программистов)

Бьярн Струструп выпустил статью под названием "Concept-Based Generic Programming in C++", о том, что такое концепты (нововведение в C++ последних лет) и как они помогают писать код с темплейтами еще лучше, чем раньше. Если в двух словах, концепты это способ формулировать требования к типам (например, "тип T обязан быть численным" или "обязан поддерживать операцию < и возвращать bool"), чтобы можно было эти требования включать в темплейты.

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

Кажется, что для Струструпа идеальное использование C++ выглядит так. Рядовой программист использует классы, функции итд., которые написали для него некие волшебники за кулисами. Для него все вылизано и сделано максимально простым: он объявляет переменные каких-то типов, делает с ними операции, которые удобно записываются как + или <<, и так далее. Если он что-то написал неправильно, то в 90% случаев это даже не скомпилируется, а если скомпилируется, то кинет исключение, но ему для этого ничего не нужно особенно делать. При этом волшебники за кулисами, чтобы все это сделать, написали тонны крайне нечитаемого и сложного кода, который делает головокружительную эквилибристику, жонглирует мета-программированием, использует всякие подспорные std::штуки и "двойные" ссылки и ключевое слово "requires", которые рядовой программист в обычной жизни вообще никогда не видит. Почему-то оба эти языка, тот, на котором пишет программист и волшебник за кулисами, называются C++.

С моей точки зрения это ужасно, с точки зрения Струструпа - красиво и правильно. Более того - неизбежно вытекает из твердых принципов, которые он повторяет в начале статьи (generality, uncompromised efficiency, statically type-safe interfaces - совместимость тут даже не упоминается, как само собой разумеющееся, видимо).

Например, он дает пример класса Span, который заключает в себе идею контейнера с прямым доступом по индексу, проверяющего, что индекс не выходит за пределы. Span можно инициализировать обычным массивом с размером, известным во время компиляции, или вектором с размером, доступным по size(), и он будет работать одинаково просто и удобно, и кидать исключение в случае слишком большого индекса. Если мы а) несогласны вносить проверку границ в сам язык (uncompromised efficiency), но при этом хотим дать к ней удобный доступ всем, кто хочет и не ограничиться одними массивами или векторами, а вообще для всего (generality), и при этом для разных видов Span автоматически компилировать разный код для доступа к реальным данным и проверки их длины (statically type-safe interfaces), то мы приходим к необходимости наворотить кучу сложной магии за кулисами, чтобы можно было написать:

double arr[10];
Span s(arr);
s[20] // throws

При этом Струструп отдельно с гордостью поясняет, что, казалось бы, надо писать Span<double> s(arr), потому что темплейт-классу Span необходимо знать тип элемента в контейнере, но пользуясь отдельным специальным магическим заворотом, можно сделать так, что эта информация выцепляется из arr статически. По-моему это только все ухудшает, потому что не понимаешь даже, что используешь темплейтный класс. По Струструпу, это правильно, мне и не нужно это понимать, мне надо пользоваться API, которые наваяли волшебники, и не отсвечивать.

Внутренности Span частично выглядят так:
template<class T>
class Span {
T* p;
unsigned n;
Span(std::ranges::continuous_range auto& s) : p{data(s)}, n{size(s)} {}
}

T.e. Span хранит указатель на начало данных, правильного типа, и их размер; но откуда он их берет? Для контейнера s он вызывает data(s) и size(s) - откуда они берутся?
data() и size() - темплейтные функции, которые по-разному написаны для массива и для вектора (итп.), сидят в std::range - то, что для них не указан std::range это еще один вид магии, так называемый ADL, крайне неприятный. А std::ranges::continuous_range это как раз и есть концепт, который говорит в своем определении, что тип обязан предоставить data() и size() - "предоставить" не как в ООП, не как методы в классе, а просто чтобы были, чтобы data(s) и size(s) компилировалось и давало правильный тип.

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

Отдельный интерес представляет седьмая глава, где Струструп рассказывает немного о том, как не только концепты, но и темплейты вообще появились в языке, как развивались и почему они выглядят так, а не иначе. Вот в этой цитате отчетливо ощущается раздражение дизайнера языка. Он говорит о том, что вообще хотел обойтись без ключевого слова "template":

I chose the < … > notation for type parameterization following some use in theory. Initially, I did not use the prefix template keyword: < … > was a suffix to the name they parameterized. However, people strongly insisted on having a prefix keyword to make templates stand out. That is typical, initially people ask for a LOUD syntax for novel constructs because they are seen as difficult or even dangerous. Later, the same people complain about verbosity.

Date: 2025-10-23 07:25 pm (UTC)
vak: (Default)
From: [personal profile] vak
Мне кажется, у Си++ уже нет шансов. Дремучие аксакалы загубят язык.

Date: 2025-10-25 10:39 am (UTC)
From: [identity profile] especialhifi3.livejournal.com
Кажется Страуструп открыл для себя функциональное программирование.

концепты это способ формулировать требования к типам (например, "тип T обязан быть численным" или "обязан поддерживать операцию < и возвращать bool"), чтобы можно было эти требования включать в темплейты.


Это очень круто. В Хаскелле такое давно есть. Хотел бы я, чтобы в моем любимом F# было такое. Есть только суррогат типа интерфейсов, но это не то.

По Струструпу, это правильно, мне и не нужно это понимать, мне надо пользоваться API, которые наваяли волшебники, и не отсвечивать.


Это Страуструп писал, что не надо понимать, или ваш вывод исходя из того, что в синтаксисе нет всякой ненужной фигни?

Понимать, конечно, нужно. Но делать синтаксис длинным чтобы дурак-программист все-таки понял, что он пишет - тупиковый путь. Дурак-программист всегда найдет, что не понять. И если затачивать язык под дураков, то а) умным на нем писать неприятно будет, б) дураки станут еще глупее (дураки опускаются до минимально возможного уровня интеллекта, при котором они не умирают).

По-моему это только все ухудшает, потому что не понимаешь даже, что используешь темплейтный класс.


В мейнстриме (C#), и в F#, это уже давным-давно есть. Если программист не понимает, то пусть лучше займется каким-нибудь другим занятием.

Кажется, что для Струструпа идеальное использование C++ выглядит так. Рядовой программист использует классы, функции итд., которые написали для него некие волшебники за кулисами. Для него все вылизано и сделано максимально простым


Не знаю что именно пишет Струструп, но это замечательная идея, которая используется в ФП. Вы ее не совсем правильно поняли. Идея не в том, что есть два программиста. Идея в том, что программист пишет параллельно т.н. "системный код", который может выглядеть ужасно, но его мало. С помощью него он создает по сути мини-язык - элегантный, красивый, мощный. А всю бизнес-логику, которой дохрена, он пишет на этом мини-языке. В итоге бизнес-логика легко читается и легко изменяется. А в "системный код" может быть и не понадобится лезть или его менять годами. Программистов может быть и два, но не обязательно. И да, программист, который пишет бизнес-логику должен понимать, как работает "системый код", ибо абстракции протекают.
Edited Date: 2025-10-25 10:41 am (UTC)

February 2026

S M T W T F S
1 2 3 4 5 67
8 9 10111213 14
15 16 17 18192021
2223 24 25262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 26th, 2026 04:25 pm
Powered by Dreamwidth Studios