TGViewer
this->notes. this->notes. @thisnotes · 4.53K subscribers
Post #189 1.6K
#common

О подходах к реализации шаблонов/дженериков.

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

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

В Java используется стирание типов (type erasure).

public class A<T> {
private T obj;


public A(T t){
obj = t;
}

public T getObj() {
return obj;
}
}


Однако в рантайме дженерик-типа не остаётся. Класс будет существовать в таком виде:

public class A {
private Object obj;


public A(Object t){
obj = t;
}

public Object getObj() {
return obj;
}
}


Т.е. в дженерик-типе/функции T заменяется на Object, а в месте использования происходит следующее:

A<String> a = new A<>("qwe");
var aa = a.get();


заменится на

A a = new A("qwe");
var aa =
(String) a.get();

Т.е. снаружи есть информация о том, какой тип отдавался в дженерик, что позволяет сделать нужный каст. И тут есть одно очевидное преимущество: вы можете не перекомпилировать пакет с определением дженерика. Однако сразу можно заметить и недостатки: т.к. происходит стирание типа до Object (который является родительским типом для всех типов, кроме примитивных), дженерики нельзя использовать с примитивными типами (ха-ха, лохи). Потому и появились типы-обёртки вроде Integer, Boolean и прочих.

Хотя это пытаются пофиксить. Как база используется новый вид классов (неожиданно, примитивный). Вы объявляете свой класс с ключевым словом primitive. Для таких классов появляются правила конвертации в примитивные типы, запрет на наличие значения Null и другие разные правила. Но сейчас не об этом. Пропозал (хз насколько такой термин в сообществе джавы применим).

Когда у вас есть примитивные классы, вы можете интерпретировать примитивные типы вроде int/double и другие как примитивные классы, что позволит реализовать дженерики и для них. Получается, что костылят :(
Хотели ввести в Java 19, но не успели, так что мб к Java 20 получится. Линк.

В Rust дженерики работают примерно как в плюсах: на один крейт (единица компиляции в Rust) тип в дженерик подставляется только один раз (грубо говоря, копирование кода для каждого типа в дженерике). Если дженерик из крейта 1 используется в крейте 2, то будет своя инстанцированная версия в крейте 2.

В go всё примерно так же за тем исключением, как работают интерфейсы относительно трейтов в rust. Тип на “подходимость” к интерфейсу проверяется автоматически, тогда как тот факт, что тип удовлетворяет трейту, надо указывать ручками.

UPD: тема go не раскрыта. Чуть позже вкину подробный пост про это в качестве исправления.
  • 👍 7
  • 🐳 4
  • ❤ 1
More from @thisnotes
  1. Sep 17, 2026#common Сидите вы себе спокойно, разрабатываете поиск каких-нибудь объектов. Может это тов…
  2. Sep 9, 2026#cpp #books Да, книга 2001ого года. Мы ровесники. И да, в ней в основном обсуждаются какие…
  3. Sep 2, 2026#perf Попробовал собрать в кучку (кажется, немного сумбурно всё же) мысли по двум моментам…
  4. Aug 31, 2026Давайте новый тег заведём: #perf Во-первых, надо понять, что я вообще понимаю под перфом,…
  5. Aug 27, 2026#common Мы часто делаем системы, которые обладают какими-то ограничениями. Ограничения наш…
  6. Aug 24, 2026#list 0. [talk] Achieving Peak Performance for Matrix Multiplication in C++. Aliaksei Sala…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →