Si suppone che i tipi basati su modelli seguano un "concetto" (Iteratore di input, Iteratore in avanti, ecc ...) in cui i dettagli effettivi del concetto sono definiti interamente dall'implementazione della funzione / classe del modello e non dalla classe del tipo usato con il modello, che è un po 'anti-uso di OOP.
Penso che tu fraintenda l'uso previsto dei concetti con i modelli. Forward Iterator, ad esempio, è un concetto molto ben definito. Per trovare le espressioni che devono essere valide affinché una classe possa essere un Iteratore in avanti e la loro semantica inclusa la complessità computazionale, guardate lo standard o http://www.sgi.com/tech/stl/ForwardIterator.html (devi seguire i collegamenti a Input, Output e Trivial Iterator per vedere tutto).
Quel documento è un'interfaccia perfettamente valida e "i dettagli reali del concetto" sono definiti proprio lì. Non sono definiti dalle implementazioni di Forward Iterator, né sono definiti dagli algoritmi che utilizzano Forward Iterator.
Le differenze nel modo in cui vengono gestite le interfacce tra STL e Java sono tre volte:
1) STL definisce espressioni valide usando l'oggetto, mentre Java definisce metodi che devono essere richiamabili sull'oggetto. Naturalmente un'espressione valida potrebbe essere una chiamata di metodo (funzione membro), ma non deve esserlo.
2) Le interfacce Java sono oggetti di runtime, mentre i concetti STL non sono visibili in fase di runtime anche con RTTI.
3) Se non si riescono a rendere valide le espressioni valide richieste per un concetto STL, si ottiene un errore di compilazione non specificato quando si crea un'istanza di un modello con il tipo. Se non si implementa un metodo richiesto di un'interfaccia Java, viene visualizzato un errore di compilazione specifico che lo dice.
Questa terza parte è se ti piace una sorta di "tipizzazione" (in fase di compilazione): le interfacce possono essere implicite. In Java, le interfacce sono in qualche modo esplicite: una classe "è" Iterabile se e solo se dice che implementa Iterable. Il compilatore può verificare che le firme dei suoi metodi siano tutte presenti e corrette, ma la semantica è ancora implicita (cioè sono documentate o meno, ma solo più codici (unit test) possono dirti se l'implementazione è corretta).
In C ++, come in Python, sia la semantica che la sintassi sono implicite, sebbene in C ++ (e in Python se si ottiene il preprocessore di tipo forte) si ottiene un aiuto dal compilatore. Se un programmatore richiede una dichiarazione esplicita di interfacce simile a Java da parte della classe di implementazione, l'approccio standard consiste nell'utilizzare tratti di tipo (e l'ereditarietà multipla può impedire che ciò sia troppo dettagliato). Ciò che manca, rispetto a Java, è un singolo modello che posso istanziare con il mio tipo e che compilerà se e solo se tutte le espressioni richieste sono valide per il mio tipo. Questo mi direbbe se ho implementato tutti i bit richiesti, "prima di usarlo". Questa è una comodità, ma non è il nucleo di OOP (e continua a non testare la semantica,
STL può essere o meno sufficientemente OO per i tuoi gusti, ma certamente separa l'interfaccia in modo chiaro dall'implementazione. Non ha la capacità di Java di riflettere sulle interfacce e segnala diversamente le violazioni dei requisiti di interfaccia.
puoi dire la funzione ... si aspetta un Iteratore in avanti solo guardando la sua definizione, dove avresti bisogno di guardare l'implementazione o la documentazione per ...
Personalmente penso che i tipi impliciti siano un punto di forza, se usati in modo appropriato. L'algoritmo dice cosa fa con i suoi parametri template, e l'implementatore si assicura che quelle cose funzionino: è esattamente il comune denominatore di cosa dovrebbero fare le "interfacce". Inoltre, con STL, è improbabile che tu stia usando, diciamo, in std::copy
base alla ricerca della sua dichiarazione in avanti in un file di intestazione. I programmatori dovrebbero capire cosa prende una funzione in base alla sua documentazione, non solo alla firma della funzione. Questo è vero in C ++, Python o Java. Ci sono limitazioni su ciò che può essere ottenuto digitando in qualsiasi lingua e provare a usare la digitazione per fare qualcosa che non fa (controllare la semantica) sarebbe un errore.
Detto questo, gli algoritmi STL di solito denominano i parametri del modello in modo da chiarire quale concetto è richiesto. Tuttavia, ciò serve a fornire utili informazioni supplementari nella prima riga della documentazione, non a rendere le dichiarazioni più informative. Ci sono più cose che devi sapere di quelle che possono essere incapsulate nei tipi di parametri, quindi devi leggere i documenti. (Ad esempio negli algoritmi che accettano un intervallo di input e un iteratore di output, è probabile che l'iteratore di output abbia bisogno di "spazio" sufficiente per un certo numero di output in base alla dimensione dell'intervallo di input e forse ai valori ivi contenuti. Prova a scriverlo con forza. )
Ecco Bjarne su interfacce dichiarate esplicitamente: http://www.artima.com/cppsource/cpp0xP.html
In generici, un argomento deve appartenere a una classe derivata da un'interfaccia (l'equivalente C ++ all'interfaccia è una classe astratta) specificato nella definizione del generico. Ciò significa che tutti i tipi di argomenti generici devono rientrare in una gerarchia. Ciò impone vincoli non necessari ai progetti richiede una previsione irragionevole da parte degli sviluppatori. Ad esempio, se scrivi un generico e definisco una classe, le persone non possono usare la mia classe come argomento per il tuo generico a meno che non conosca l'interfaccia che hai specificato e da cui ne abbia derivato la classe. È rigido.
Guardandolo al contrario, con la digitazione di anatra è possibile implementare un'interfaccia senza sapere che l'interfaccia esiste. Oppure qualcuno può scrivere un'interfaccia deliberatamente in modo tale che la tua classe la implementi, dopo aver consultato i tuoi documenti per vedere che non chiedono nulla che non fai già. Flessibile.