Предполагается, что шаблонные типы должны следовать «концепции» (Input Iterator, Forward Iterator и т. д.), в которой фактические детали концепции полностью определяются реализацией функции / класса шаблона, а не классом типа. используется с шаблоном, который несколько запрещает использование ООП.
Я думаю, что вы неправильно понимаете предполагаемое использование концепций шаблонами. Например, Forward Iterator - это очень четкое понятие. Чтобы найти выражения, которые должны быть действительными, чтобы класс являлся прямым итератором, и их семантику, включая сложность вычислений, вы посмотрите на стандарт или на http://www.sgi.com/tech/stl/ForwardIterator.html. (Вы должны перейти по ссылкам на Input, Output и Trivial Iterator, чтобы увидеть все это).
Этот документ является очень хорошим интерфейсом, и «реальные детали концепции» определены здесь же. Они не определяются реализациями прямых итераторов и не определяются алгоритмами, использующими прямые итераторы.
Различия в том, как обрабатываются интерфейсы между STL и Java, имеют три аспекта:
1) STL определяет допустимые выражения, используя объект, тогда как Java определяет методы, которые должны вызываться на объекте. Конечно, допустимым выражением может быть вызов метода (функции-члена), но это не обязательно.
2) Java-интерфейсы являются объектами времени выполнения, тогда как концепции STL не видны во время выполнения даже с RTTI.
3) Если вы не в состоянии сделать правильными требуемые допустимые выражения для концепции STL, вы получите неопределенную ошибку компиляции, когда создаете экземпляр шаблона с типом. Если вам не удается реализовать обязательный метод интерфейса Java, вы получаете конкретную ошибку компиляции, говорящую об этом.
Эта третья часть, если вам нравится своего рода (тип компиляции) "утиная типизация": интерфейсы могут быть неявными. В Java интерфейсы являются несколько явными: класс «is» Iterable, если и только если он говорит, что он реализует Iterable. Компилятор может проверить, что все сигнатуры его методов присутствуют и являются правильными, но семантика все еще неявна (т.е. они либо документированы, либо нет, но только больше кода (модульных тестов) может сказать вам, является ли реализация правильной).
В C ++, как и в Python, семантика и синтаксис неявны, хотя в C ++ (и в Python, если вы получаете препроцессор строгой типизации) вы получаете некоторую помощь от компилятора. Если программисту требуется явное объявление интерфейсов в Java-стиле реализующим классом, тогда стандартным подходом является использование признаков типа (а множественное наследование может предотвратить это слишком многословно). По сравнению с Java не хватает одного шаблона, который я могу создать для своего типа и который будет скомпилирован тогда и только тогда, когда все необходимые выражения допустимы для моего типа. Это скажет мне, реализовал ли я все необходимые биты, «прежде чем я его использую». Это удобно, но это не ядро ООП (и оно все еще не проверяет семантику,
STL может или не может быть достаточно ОО на ваш вкус, но он, безусловно, четко отделяет интерфейс от реализации. Ему не хватает способности Java отражать интерфейсы, и он по-разному сообщает о нарушениях интерфейса.
Вы можете сказать, что функция ожидает Forward Iterator, только взглянув на его определение, где вам нужно будет либо посмотреть на реализацию, либо на документацию для ...
Лично я считаю, что неявные типы являются сильной стороной при правильном использовании. Алгоритм говорит, что он делает со своими параметрами шаблона, и разработчик следит за тем, чтобы эти вещи работали: это в точности общий знаменатель того, что должны делать «интерфейсы». Кроме того, с STL вы вряд ли будете использовать, скажем, std::copy
основанный на поиске его форвард-декларации в заголовочном файле. Программисты должны выяснить, что берет функция, основываясь на ее документации, а не только на сигнатуре функции. Это верно в C ++, Python или Java. Существуют ограничения на то, что может быть достигнуто с помощью набора текста на любом языке, и попытка использовать набор для выполнения того, чего он не делает (проверьте семантику), будет ошибкой.
Тем не менее, алгоритмы STL обычно называют свои параметры шаблона таким образом, чтобы было понятно, какая концепция требуется. Однако это делается для того, чтобы предоставить полезную дополнительную информацию в первой строке документации, а не для того, чтобы сделать предварительные декларации более информативными. Есть больше вещей, которые вы должны знать, чем могут быть включены в типы параметров, поэтому вы должны прочитать документы. (Например, в алгоритмах, которые принимают входной диапазон и выходной итератор, есть вероятность, что выходному итератору нужно достаточно «пространства» для определенного количества выходных данных, исходя из размера входного диапазона и, возможно, его значений. Попробуйте ввести его строго. )
Вот Бьярне о явно объявленных интерфейсах: http://www.artima.com/cppsource/cpp0xP.html
В обобщениях аргумент должен иметь класс, производный от интерфейса (эквивалент C ++ для интерфейса является абстрактным классом), указанного в определении универсального объекта. Это означает, что все общие типы аргументов должны вписываться в иерархию. Это налагает ненужные ограничения на проекты, требует неразумного предвидения со стороны разработчиков. Например, если вы пишете универсальный шаблон, а я определяю класс, люди не смогут использовать мой класс в качестве аргумента для вашего универсального шаблона, если я не знаю об указанном вами интерфейсе и не выведу из него мой класс. Это жестко.
Если взглянуть на это с другой стороны, с помощью утки, вы можете реализовать интерфейс, не зная, что интерфейс существует. Или кто-то может написать интерфейс намеренно, чтобы ваш класс реализовал его, проконсультировавшись с вашими документами, чтобы убедиться, что они не просят ничего, чего вы еще не делали. Это гибко.