29 April 2006
Templates as type-checked dynamic typing
In C++, templates provide compile-time polymorphism (similar to parametric polymorphism) by expanding templates into different type-checked functions and types during compilation. std::vector<MyObject> is a unique vector<> type because it implements the required interface, and the determination of the "derived" vector<MyObject> type occurs at compile-time. Circle is a unique Shape type for the same reason, but the determination of derived type (given a Shape reference) occurs at run-time.
Using MyObject within vector<> means that you've entered into a contract stating that MyObject will provide those features that vector<> expects. This contract is different from polymorphism using inheritance because the is-a relationship does not exist. Types used within templates do not need to exist in a specific class hierarchy. However, those types must contain an implied interface--one which the template expects. This implied interface is managed similar to how dynamically typed languages manage accessing interfaces on class instances. A dynamically typed language only determines if FunctionA() exists on MyObject at run-time and at the point of the call. With templates, FunctionA() is only required at the point of the call, but checking occurs when the code is compiled. In both dynamically typed languages and statically typed templates, an object can be missing the required method if the code never gets called.
Using the following class (in pseudo-code):
class MyClass { function set(String param1) {do something;} }
In a dynamically typed language, a specific interface is validated only if the code is called:
function FunctionA(param1) { param1.set("a string"); } function FunctionB(param1) { // Run-time error here, only if MyClass passed in. param1.set(23); } MyClass myClass; FunctionA(myClass);
With run-time polymorphism, a specific interface is required but accessed only when the type is used:
class AnotherClass extends MyClass { } function FunctionA(MyClass & param1) { param1.set("a string"); } function FunctionB(MyClass & param1) { // Compile-time error here, even though it is never called. param1.set(23); } AnotherClass anotherClass; FunctionA(anotherClass);
With compile-time polymorphism, a specific interface is validated only the the type is used:
template MyTemplate<type MYTYPE> { function FunctionA(MYTYPE param1) { param1.set("a string"); } function FunctionB(MYTYPE param1) { // Compile-time error here, only if this method is referenced. param1.set(23); } } MyTemplate<MyClass> myTemplate; MyClass myClass; myTemplate.FunctionA(myClass);
This gives templates the flavor of dynamic typing (classes need not be of a specific type) but with the benefits of static type checking.
- Techniques after using Swift for a month posted by sstrader on 26 August 2015 at 11:41:51 PM
- Some thoughts on Ruby after finishing a week (minus one day) of training posted by sstrader on 15 December 2011 at 8:59:30 PM
- Links on the singleton pattern posted by sstrader on 9 December 2011 at 9:19:50 AM
- Phonebot posted by sstrader on 29 October 2011 at 6:37:05 PM
- The labeled break in Java posted by sstrader on 4 September 2011 at 11:27:56 AM