Last week, I said there was a tension between expressiveness and safety. Permissive types allow more programs to be written; restrictive types allows stricter reasoning about programs. We see this tension when we compare parametric polymorphism in C++ and ML.
C++ templates are extremely permissive. Here's the C++ equivalent of ML's 'a list:
template <class T> class ListNode { private: T* hd; ListNode* tl; public: ListNode(T * head, ListNode * tail) : hd(head), tl(tail) {} T* head() { return hd; } ListNode* tail() { return tl; } }; ListNode<int> * nums = new ListNode<int>(new int(10), new ListNode<int>(new int(30), NULL));
C++ instantiates templates using a process that is essentially
textual substitution. When you use the type
ListNode<int>
, the compiler automatically
generates the following class:
class ListNode__int__ { private: int* hd; ListNode__int__* tl; public: ListNode__int__(int * head, ListNode__int__ * tail) : hd(head), tl(tail) {} int* head() { return hd; } ListNode__int__* tail() { return tl; } };
Obviously, the client code needs to be changed as well:
ListNode__int__ * nums = new ListNode__int__(new int(10), new ListNode__int__(new int(30), NULL));
The names of these template instantiations will be mangled by the compiler, so as not to conflict with any programmer-defined classes.
It is also possible to define template functions. Here's the function length over lists:
template <class List> int length(List * list) { if (list == NULL) { return 0; } else { return 1 + length(list->tail()); } } cout << length< ListNode<int> >(nums); // USAGE