Fri, 18 May 2001 11:25:14 +0200, Jerzy Karczmarczuk <karczma@info.unicaen.fr> pisze:
Always when somebody mentions templates in presence of a True Functionalist Sectarian, the reaction is "What!? Abomination!!".
They aren't that wrong, but they have some problems: * It's not specified what interface they require from types to be instantiated to. * The whole implementation must be present in a public header file. Well, there is the 'export' keyword, but no compiler implements it. It follows that all things used by implementation of a template must be included too. And compilation time of a large project is larger than necessary. * They introduce very complex rules about name lookup (environment in which a specialized template is compiled is a weird mix of environments of the point of definition and point of usage of the template), deduction which overloading to use (with choosing the candidate according to partial ordering of better fitting), deduction of template types and what partial specialization to use, complicated by the fact that there are complex rules of implicit conversions. Templates don't obey a Haskell's rule that adding code doesn't change the meaning of previously valid code and can at most introduce an ambiguity which is flagged as error. * Types can't be deduced from the context of usage. For example handling the empty set or monadic 'return' would require either spelling the type at each use or introducing another type and deferring deduction of the real type until an operation is performed with a value of a known type. * Since they are similar to macros, they tend to give horrible error messages which mentions things after expansion. Here is what I once got: mico-c++ -Wall -I. -c serwer.cc -o serwer.o serwer.cc: In method `void Firma_impl::usun(const char *)': serwer.cc:92: `struct __rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>' has no member named `second' serwer.cc:93: no matching function for call to `map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase (__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,const pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *> &)' /usr/include/g++/stl_map.h:156: candidates are: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>) /usr/include/g++/stl_map.h:157: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(const string &) /usr/include/g++/stl_map.h:158: map<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *,less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >,__default_alloc_template<true,0> >::erase<string, Katalog *, less<basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> > >, alloc>(__rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> *>, __rb_tree_iterator<pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *>,pair<const basic_string<char,string_char_traits<char>,__default_alloc_template<true,0> >,Katalog *> &,pair<const basic_string<char,string_char_traits<char>, __default_alloc_template<true,0> >,Katalog *> *>) make: *** [serwer.o] Error 1 They also have some advantages over mechanisms available in Haskell: * A templatized class can contain types, values with arbitrary types, other templates etc. so it's easy to have parametrization of all things by types. Haskell requires fundeps, dummy arguments and local quantifiers to handle these things - sometimes it's not nice. * A template can be parametrized by values of primitive types (by expressions using builtin operations which can be evaluated at compile time). Yes, it's ugly that types and operations don't have equal rights, but it's sometimes useful. * A template is in practice guaranteed to be specialized to used types, so they don't have a performance overheads over non-templatized variants. My not-quite-done-right attempt at inlining dictionary functions in ghc is a step in this direction, as is the proposal of export-unfolding-and-specialize-but-not-necessarily-inline. I hope that SimonPJ will sort this out. -- __("< Marcin Kowalczyk * qrczak@knm.org.pl http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK