шаблонні типи повинні слідувати "концепції" (вхідний ітератор, перехідний ітератор тощо), де фактичні деталі концепції визначаються повністю реалізацією функції / класу шаблону, а не класом типу використовується з шаблоном, який є деяким антивикористанням OOP.
Думаю, ви неправильно розумієте використання шаблонів із застосуванням понять. Наприклад, Forte Iterator - це дуже чітко визначена концепція. Щоб знайти вирази, які повинні бути дійсними для того, щоб клас був ітератором Forward, та їх семантикою, включаючи обчислювальну складність, ви дивитесь на стандарт або на http://www.sgi.com/tech/stl/ForwardIterator.html (ви повинні перейти за посиланнями на вхід, вихід та тривіальний ітератор, щоб побачити все це).
Цей документ є ідеально хорошим інтерфейсом, і "фактичні деталі концепції" визначені саме там. Вони не визначаються реалізацією Forte Iterators, а також вони не визначаються алгоритмами, які використовують Forward Iterators.
Відмінності в обробці інтерфейсів між STL та Java тричі:
1) STL визначає дійсні вирази за допомогою об'єкта, тоді як Java визначає методи, які повинні бути викликані на об'єкті. Звичайно, допустимим виразом може бути виклик методу (член-функція), але цього не повинно бути.
2) Інтерфейси Java - це об'єкти виконання, тоді як STL концепції не видно навіть під час виконання RTTI.
3) Якщо вам не вдасться зробити дійсними необхідні дійсні вирази для концепції STL, ви отримаєте невизначену помилку компіляції, коли інстанціюєте деякий шаблон із типом. Якщо вам не вдалося реалізувати потрібний метод інтерфейсу Java, ви отримаєте певну помилку компіляції, яка сказала це так.
Ця третя частина - якщо вам подобається якийсь час (компіляція) "набору качок": інтерфейси можуть бути неявними. У Java інтерфейси дещо явні: клас "є" Ітерабельний, якщо і лише тоді, коли він каже, що реалізує Iterable. Компілятор може перевірити, чи всі підписи його методів є присутніми та правильними, але семантика все ще неявна (тобто вони задокументовані чи ні, але лише більше коду (одиничні тести) може сказати вам, чи правильна реалізація).
У C ++, як і в Python, і семантика, і синтаксис неявні, хоча в C ++ (і в Python, якщо ви отримуєте сильний набір препроцесора), ви отримуєте певну допомогу від компілятора. Якщо програміст вимагає явного декларування інтерфейсів, схожих на Java, класом реалізації, то стандартним підходом є використання ознак типу (і багатократне успадковування може запобігти занадто багатослівне). Чого бракує, порівняно з Java, - це єдиний шаблон, який я можу інстанціювати зі своїм типом, і який буде компілюватися, якщо і лише якщо всі необхідні вирази дійсні для мого типу. Це дозволить мені сказати, чи я реалізував усі необхідні біти, "перш ніж це використовувати". Це зручність, але це не суть OOP (і вона все ще не перевіряє семантику,
STL може бути або не бути достатньо OO на ваш смак, але він, безумовно, відокремлює інтерфейс чисто від реалізації. Йому не вистачає можливості Java робити відображення через інтерфейси, і він повідомляє про порушення вимог інтерфейсу по-різному.
Ви можете сказати, що функція ... очікує, що Фортератор ітератора лише переглянувши його визначення, де вам потрібно буде переглянути або реалізацію або документацію для ...
Особисто я вважаю, що неявні типи є силою, якщо їх використовувати належним чином. Алгоритм говорить про те, що він робить зі своїми параметрами шаблону, а реалізатор гарантує, що ці речі працюють: це саме загальний знаменник того, що повинні робити "інтерфейси". Крім того, зі STL ви навряд чи будете використовувати, скажімо, std::copy
на основі знаходження своєї прямої заяви у файлі заголовка. Програмістам слід розробити те, що функція приймає на основі її документації, а не лише на підписі функції. Це вірно в C ++, Python або Java. Існують обмеження щодо того, чого можна досягти при введенні будь-якою мовою, і намагання використовувати введення тексту, щоб зробити те, чого він не робить (перевірити семантику), було б помилкою.
Однак, алгоритми STL зазвичай називають параметри шаблону таким чином, що дає зрозуміти, яка концепція потрібна. Однак це полягає в наданні корисної додаткової інформації в першому рядку документації, а не в тому, щоб робити декларації більш інформативними. Ви повинні знати більше речей, ніж їх можна інкапсулювати у типи параметрів, тому вам доведеться читати документи. (Наприклад, в алгоритмах, які приймають діапазон введення та ітераторі виводу, ймовірність того, що ітератору виходу потрібно достатньо «місця» для певної кількості виходів, виходячи з розміру діапазону введення та, можливо, значень в ньому. Спробуйте сильно ввести це. )
Ось Bjarne на явно оголошених інтерфейсах: http://www.artima.com/cppsource/cpp0xP.html
У дженериках аргумент повинен бути класу, похідного від інтерфейсу (еквівалент C ++ інтерфейсу - абстрактний клас), визначеного у визначенні загального. Це означає, що всі типи загальних аргументів повинні вписуватися в ієрархію. Це обмежує непотрібні обмеження дизайну і вимагає необґрунтованого передбачення з боку розробників. Наприклад, якщо ви пишете загальний, а я визначаю клас, люди не можуть використовувати мій клас як аргумент до вашого загального, якщо я не знав про вказаний вами інтерфейс і отримав з нього мій клас. Це жорстко.
Дивлячись на це навпаки, при наборі качок ви можете реалізувати інтерфейс, не знаючи, що інтерфейс існує. Або хтось може навмисно написати інтерфейс, щоб ваш клас реалізував його, проконсультувавшись із вашими документами, щоб побачити, що вони не просять нічого, чого ви ще не робите. Це гнучко.