Topic: Specializing member function templates (was Template virtual function?)


Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 2 May 1994 01:45:12 GMT
Raw View
In article <CoJ575.Gs0@ses.com> jamshid@ses.com (Jamshid Afshar) writes:
>In article <WIKMAN.94Apr18084724@king.trs.ntc.nokia.com>,

>ANSI/ISO has made changes from the ARM.  Stroustrup writes in his new
>_The Design and Evolution of C++_ 15.10.3 p375:
>
> I concluded that specialization as originally defined was a
> botch and also provided essential functinality.  How might we
> provide he functionality while remedying the botch?  After
> many complicated arguments, I proposed a trivially simple
> solution that was accepted at the San Jose meeting: A
> specialization must be declared before it is used.  This
> simply brings specialization into line with the rules of
> ordinary overloading.  If no specialization is in scope at a
> point of use, the general template definition will be used.

 Template specialisation is STILL a botch. IMHO.

>D&E doesn't mention how template member functions are now specialized,
>but I assume it uses the old syntax since there's no such confusion:
>
>  void Templ1<float>::print() {/*...*/}
>
>I'm not sure how to "declare" this specialization without providing a
>definition.

 I guess we will just have to allow

>  void Templ1<float>::print();  // specialized definition provided in
>                                // some .cc file (???)

 as you say.

 Specialisation is DANGEROUS. Templates, unlike classes,
are not encapsulated.  If programmer 1 writes a specialisation,
and programmer 2 wants the unspecialised version of
something, then if their code is used together, what happens?

 Answer: undiagnosed error, core dump, crash.

 You may NEVER track it down if you dont have the original
sources and the two programmers didnt document their template use.
Note again; the TWO programmers.

 You MUST documemt not only what specialisations you
write/use but EVERY unspecialised template you instantiate,
so the other guy knows e may NOT specialise those things
you require unspecialised.

 In my opinion this is a ghastly design fault in the language.
It makes specialisation useless in programming in the large situations.

 Is there a solution? Yes, there is a wonderful solution
using namespaces, but there are some minor parsing problems
(NOT created by the solution but the existing syntax of template-id's)
and there are some deficiencies in namespaces that make
use of specialisations a bit messy at present.

 The solution is to ban specialisations completely,
but then allow "specialisations" to be declared in a
namespace like this:

 template<class T> void f(T){ .. }
 namespace myspace {
  void f<char>(char) { .. }
 };
 using namespace myspace;

 f('c'); // calls "specialisation"

The problem of merging the overload sets is a namespace
problem that has nothing much to do with templates.

The POINT of this is that templates are, by this proposal,
completely encapsulated and inviolate. You can have
not just one specialisation or use the generated function,
but any number of specialisations -- and they do not
interfere because they're in separate namespaces.
That is, they're just "other" functions that work by
the normal language rules. NO rules are required to
handle specialisations -- because there aren't any!

The mechanism of this proposal has an interesting side effect:

 void myfunc<char>(char) { .. }

Just fine. "name<type>" is just another name.

 class X<int> { .. }

Fine. X<int> is the name of the class. Nothing to do with templates.

And thats the point. Template specialisations are exactly that --
totally unrelated to the template except by name.

There's more. In my opinion:

 template<class T, class U> void f(T,U){ .. }
 template<class T> void f(Y, int) { .. }

the second template is CLEARLY a specialisation. So the
idea of a specialisation is all wrong -- its not just
something that applies to individual instances, but can
apply to whole families of instances. A specialisation
involves ANY restriction on the arguments, not just one
that binds the lot. [I note that this works just fine
using inheritance: each subtype is just a restriction
on its supertype, a subtype is exactly a specialisation]

 The above use has the interesting effect of
rending all uses of what was intended to be a specialisation
uncallable due to an ambiguity. Not even qualified syntax
can discriminate this case:

 int i;
 f(i,i); // ambiguous
 f<int,int>(i,i); // ambiguous

 Exactly what do specialisations give us?
The answer may be unexpected -- except in the case
of member functions or passing templates to templates,
they provide no functional power whatsoever.

 They provide a convenient naming convention,
that is, referential transparency.

 Its well known that referential transparency is a two edged
sword -- it can hide implementation details and save remembering
lots of horrid names. And it can obsure important semantic differences.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: g2devi@cdf.toronto.edu (Robert N. Deviasse)
Date: Mon, 2 May 1994 20:11:28 GMT
Raw View
In article <Cp5JJC.E4H@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
> Specialisation is DANGEROUS. Templates, unlike classes,
>are not encapsulated.  If programmer 1 writes a specialisation,
>and programmer 2 wants the unspecialised version of
>something, then if their code is used together, what happens?
>
> Answer: undiagnosed error, core dump, crash.
>
> You may NEVER track it down if you dont have the original
>sources and the two programmers didnt document their template use.
>Note again; the TWO programmers.
>
> You MUST documemt not only what specialisations you
>write/use but EVERY unspecialised template you instantiate,
>so the other guy knows e may NOT specialise those things
>you require unspecialised.
>
> In my opinion this is a ghastly design fault in the language.
>It makes specialisation useless in programming in the large situations.

[...]

>
>The POINT of this is that templates are, by this proposal,
>completely encapsulated and inviolate. You can have
>not just one specialisation or use the generated function,
>but any number of specialisations -- and they do not
>interfere because they're in separate namespaces.
>That is, they're just "other" functions that work by
>the normal language rules. NO rules are required to
>handle specialisations -- because there aren't any!
>
>The mechanism of this proposal has an interesting side effect:
>
> void myfunc<char>(char) { .. }
>
>Just fine. "name<type>" is just another name.
>
> class X<int> { .. }
>
>Fine. X<int> is the name of the class. Nothing to do with templates.
>
>And thats the point. Template specialisations are exactly that --
>totally unrelated to the template except by name.
>
>There's more. In my opinion:
>
> template<class T, class U> void f(T,U){ .. }
> template<class T> void f(Y, int) { .. }
>
>the second template is CLEARLY a specialisation. So the
>idea of a specialisation is all wrong -- its not just
>something that applies to individual instances, but can
>apply to whole families of instances. A specialisation
>involves ANY restriction on the arguments, not just one
>that binds the lot. [I note that this works just fine
>using inheritance: each subtype is just a restriction
>on its supertype, a subtype is exactly a specialisation]
>

I agree with much of what is said, but I don't think templates should be
abandoned, then should be fixed. I don't think that it requires too much
work.

Personally, I like to view template specialization as a different form of
inheritance and I think that the syntax *and semantics* of templates should
reflect this.

        template<class T, class U>
            void f(T,U){ ... }

        template<class T>  : <T,int>
            void f(T, int) { ... }

        template<>  : <char,int>
            void f(char, int) { ... }


        template<class T, class U>
            class C { ... };

        template<class T>  : <T,int>
            class C { ... };

        template<>  : <char,int>      // This inherits from
            class C { ... };          //     template<class T, class U>  class C {};
       /*
          template<>  : <char>        // This inherits from
              class C { ... };        //     template<class T>  class C {};
       */


Given the "template specialization" == "template inheritance" idea, the
specification of the template should be inherited also. This means:
        template<class T>
           struct Eg {
              typedef const T& argT;

              void print(argT t) const { cout << t ; }
              void read(T& t) const    { cin  >> t ; }
           };

        template<> : <int>
           struct Eg {
              typedef const int argT;
              typedef int something_else;
           };

should be equivalent to:
        template<class T>
           struct Eg {
              typedef const T& argT;

              void print(argT t) const { cout << t ; }
              void read(T& t) const    { cin  >> t ; }
           };

        template<> : <int>
           struct Eg {
              typedef const int argT;
              typedef int something_else;
              void print(argT t) const { cout << t ; }
              void read(T& t) const    { cin  >> t ; }
           };

It saves a tremendous about of re-coding and enforces the implementation
specification so that both the compiler's job and the programmer's jobs
are made easier. (If member functions can be specialized, why can't typedefs?)
Also, I see little difference between
       void Eg<double>::print(double d);

       void Eg<double>::print(double d) {
          cout << int(100*d+50)/100.0 ;
       }
and
       template<> : <int>
          struct Eg {
              void print(double d) {
          };

       void Eg<double>::print(double d) {
          cout << int(100*d+50)/100.0 ;
       }

other than in the second, the specializations of the member functions must be
localized which makes maintenance and compilation easier.

This syntax and semantics of the above meshes well with the syntax of assigning
default parameters to template arguments, since

        template<class T, class U=int>
            class D { ... };

would be equivalent to

        template<class T, class U>
            class D { ... };

        template<class T>  : <T,int>
            class D {};

just as:
        void f(int,int=21);
is equivalent to
        void f(int,int);
        inline void f(int x) { f(x,21); }

Any comments?


>
>--
>        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
> Maxtal Pty Ltd,      CSERVE:10236.1703
>        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
>        NSW 2131, AUSTRALIA



Take care
    Robert

--
/----------------------------------+------------------------------------------\
| Robert N. Deviasse               |"If we have to re-invent the wheel,       |
| EMAIL: g2devi@cdf.utoronto.ca    |  can we at least make it round this time"|
+----------------------------------+------------------------------------------/




Author: dag@control.lth.se (Dag Bruck)
Date: 03 May 1994 05:36:55 GMT
Raw View
>>>>> "J" == John Max Skaller <maxtal@physics.su.OZ.AU> writes:

J>  Specialisation is DANGEROUS. Templates, unlike classes, are
J> not encapsulated.  If programmer 1 writes a specialisation, and
J> programmer 2 wants the unspecialised version of something, then if
J> their code is used together, what happens?
J>
J>  Answer: undiagnosed error, core dump, crash.

You have a similar problem if you define two incompatible types with
the same name in your program.  I don't see that templates or template
specializations are any worse.

Yes, template specializations must be documented.  Redefined virtual
functions must also be documented.  I don't see that templates or
template specializations are any worse.

J>  In my opinion this is a ghastly design fault in the language.
J> It makes specialisation useless in programming in the large
J> situations.
J>
J>  Is there a solution? Yes, there is a wonderful solution using
J> namespaces, ...

Come on!  First you claim it's gastly fault that makes specializations
useless, then you provide a work-around in the next paragraph.

I think I believe more in your solution than in your asessment of the
magnitude of the problem.

J>  The solution is to ban specialisations completely, but then
J> allow "specialisations" to be declared in a namespace like this:

NOT to ban specializations...?  Oops.

      -- Dag




Author: swf@tdat.ElSegundoCA.NCR.COM (Stan Friesen)
Date: Wed, 4 May 94 09:37:08 PDT
Raw View
In article <1994May2.201128.28425@cdf.toronto.edu>, g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
|>
|> I agree with much of what is said, but I don't think templates should be
|> abandoned, then should be fixed. I don't think that it requires too much
|> work.

Actually, it is my understanding that the committee has decided upon an
even simpler solution.

They have approved requiring that a specialization be declared in order to
be used.  (No new syntax really, just the usual overload declaration).

--
swf@elsegundoca.ncr.com  sarima@netcom.com

The peace of God be with you.




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 6 May 1994 04:32:47 GMT
Raw View
In article <1994May2.201128.28425@cdf.toronto.edu> g2devi@cdf.toronto.edu (Robert N. Deviasse) writes:
>In article <Cp5JJC.E4H@ucc.su.OZ.AU> maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>> Specialisation is DANGEROUS. Templates, unlike classes,
>>are not encapsulated.  If programmer 1 writes a specialisation,
>>and programmer 2 wants the unspecialised version of
>>something, then if their code is used together, what happens?
>>
>> Answer: undiagnosed error, core dump, crash.
>>
>I agree with much of what is said, but I don't think templates should be
>abandoned, then should be fixed. I don't think that it requires too much
>work.

 I presume you meant to write 'specialisations', I never
suggested templates be abandoned. I also dont propose to prevent
specialisation, I propose to get rid of 'template specialisations'
in the form they are in now.
>
>Personally, I like to view template specialization as a different form of
>inheritance

 ABSOLUTELY! This is correct. And the word and use
of 'specialisation' is the same in both cases. After all,
inheritance and templates are both different forms of polymoprphism!

 And the REQUIREMENTS to make it operable are the same
too. They're well known, basic software engineering principles.
The most important one is the open/closed principle:
its necessary that the original template be 'closed' for use in
such a way that unspecialised versions can be used by ANY user.

 Its necessary that the template be 'open' for extension,
allowing users to write specialisations -- which dont clash
with each other OR the original unspecialised template.

 There's only one way to do that. Namespaces.

>and I think that the syntax *and semantics* of templates should
>reflect this.
>
>        template<class T, class U>
>            void f(T,U){ ... }
>
>        template<class T>  : <T,int>
>            void f(T, int) { ... }
>
>        template<>  : <char,int>
>            void f(char, int) { ... }

 AH YES! VERY NICE. You clearly agree that an 'instance'
of a template is merely a full closure. We need partial
closures, and we need to understand that any restriction
is a specialisation, and NOT just a single instance.

 For example, in todays notation:

 template<class T, class U> void f(T,U) { .. }
 template<class T>          void f(T,int) { .. }

ist CLEAR that the second function template is intended to
be a specialisation of the first. But ANY call which could
call the specialisation would be ambiguous. So we need
to use the notation:

 template<class T>          void f<T,int>(T,int) { .. }

This is logically equivalent to what you have suggested,
but more in line with current notation.
>
>Given the "template specialization" == "template inheritance" idea, the
>specification of the template should be inherited also.

[]

>It saves a tremendous about of re-coding and enforces the implementation
>specification so that both the compiler's job and the programmer's jobs
>are made easier. (If member functions can be specialized, why can't typedefs?)

 Because there is a categorical difference between
specialising an INTERFACE and an IMPLEMENTATION.

 Specialising a function or member functions or even private
variable is  IN SUCH A WAY THAT THE ORIGINAL SEMANTICS ARE PRESERVED,
is IMPLEMENTATION SPECIALISATION.  It corresponds to the notion of subtype.

Specialising an interface, such as changing a class completely,
is INTERFACE MUTATION. Its a hack. The new type bears no
relation to the old one, other than its name.

>This syntax and semantics of the above meshes well with the syntax of assigning
>default parameters to template arguments, since
>
>        template<class T, class U=int>
>            class D { ... };
>
>would be equivalent to
>
>        template<class T, class U>
>            class D { ... };
>
>        template<class T>  : <T,int>
>            class D {};
>
>just as:
>        void f(int,int=21);
>is equivalent to
>        void f(int,int);
>        inline void f(int x) { f(x,21); }
>
>Any comments?

 I'm not sure about your syntax.

 I agree completely with your analysis.

 What you have missed is that templates need to
be encapsulated somehow to close them. And the way to
do that is namespaces.

 Why? Because templates are a trick which provides
referential transparency -- that is "same nameness",
and "name spaces" just have to be the way to control access
to and meaning of names.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 6 May 1994 06:32:42 GMT
Raw View
In article <DAG.94May3073656@bellman.control.lth.se> dag@control.lth.se (Dag Bruck) writes:
>>>>>> "J" == John Max Skaller <maxtal@physics.su.OZ.AU> writes:
>
>J>  Specialisation is DANGEROUS. Templates, unlike classes, are
>J> not encapsulated.  If programmer 1 writes a specialisation, and
>J> programmer 2 wants the unspecialised version of something, then if
>J> their code is used together, what happens?
>J>
>J>  Answer: undiagnosed error, core dump, crash.
>
>You have a similar problem if you define two incompatible types with
>the same name in your program.  I don't see that templates or template
>specializations are any worse.

 There is a solution for types. Use namespaces.
If you define two types with the same name in your own namespace,
you deserve a long and difficult debugging lesson :-)

 This solution doesnt currently work for templates.
>
>Yes, template specializations must be documented.

 Inadequate! Unspecialised uses must also be documented.

>Redefined virtual
>functions must also be documented.

 IMHO: Not only is it not necessary to document
specialised versions of virtual functions, its DANGEROUS to do so
in the proper subtyping use of inheritance.  Usually, I do
NOT want people knowing which speedups I have put into the library
and which I havent. That allows me to change the implementation
details freely.

 I know its not widely accepted that overrides of
virtual functions are implementation details. But they are.
And if you write code that way it works beautifully.

 If you want to provide a new method in a derived class
with particular semantics, you should add a new, distinct function.

>I don't see that templates or
>template specializations are any worse.

 The comparison with virtual functions is why.
Templates are a form or polymorphism, specialisations
are like derived classes. You should NOT need to document
specialisations, it should make no difference to the semantics
whether you specialise or not. It just makes things faster.

 The comparison with types is more valid. But the
difference is that choosing a duplicate name is an accident.
And there is a way to check -- look at the other guy's
header files. If there's clash, you just change the name.

 With specialisations, you can look at the other guy's
header files to see if a specialisation is being used.
If there is one, you are stuffed. If there isnt, you are
puzzled because you STILL dont know if you can safely
write a specialisation. So, you need a protocol, just as for
types with external linkage you need to put them in header files
just someone else CAN check.

 The protocol for templates is to write:

 // I'm using basic_string<int>
 class basic_string<long> { ... } // specialisation

which tells the other guy he cant specialise it, and he's stuffed.
I bet YOU dont follow this protocol.

>Come on!  First you claim it's gastly fault that makes specializations
>useless, then you provide a work-around in the next paragraph.

 Its a work-around the committee can choose, not programmers.
At present, specialisation in a namespace distinct from
the template definition is not allowed.
>
>I think I believe more in your solution than in your asessment of the
>magnitude of the problem.

 Thanks. The 'magnitude' depends on whether you speak
as a programmer (its ghastly) or a committe member (there is
a relatively painless repair to the language).
>
>J>  The solution is to ban specialisations completely, but then
>J> allow "specialisations" to be declared in a namespace like this:
>
>NOT to ban specializations...?  Oops.

 I would ban "specialisations", that is, the current
way of specialising templates. I would support specialisation,
however. Does that make sense?

 I'm working on this topic BECAUSE specialisation is important.
But the current way of doing it is not so good. It was the best
we could do without namespaces. But now we have namespaces,
making template polymorphism safe is important, because it
the trend of the future. People are going to write template
specialisations tomorrow the same way they write virtual
functions and derived classes today.

 We need to avoid 'template namespace specialisation
pollution' <grin> right now. Before there is so much code
written in Standard ANSI/ISO C++ that changing to Eiffel
is the only way out.



--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: jamshid@ses.com (Jamshid Afshar)
Date: Tue, 19 Apr 1994 23:28:16 GMT
Raw View
In article <WIKMAN.94Apr18084724@king.trs.ntc.nokia.com>,
 <wikman@ntc.nokia.com> wrote:
>> pjl@graceland.att.com (Paul J. Lucas) said:
>>> In <Co9qAx.HA6@avalon.chinalake.navy.mil> werme@databrpc.chinalake.navy.mil (Todd Werme) writes:
>
>>>template <class T>
>>>class Temp1 {
>>>   public:
>>>      virtual void print(){ printf("Using default\n"); }
>>>};
>>>void Temp1<float>::print(){ printf("Using float\n"); }   // Error is here.
>
>>  Individual member function can not be specialized.  Period.
>>  You must specialize the entire class.
>
>Perhaps I'm missing something here, but I do this all the time. The
>example above works ok (cfront 3.something) if the inline is removed.
>With the inline it complains about two definitions, which I think it
>shouldn't do.

I agree the ARM intends to allow member function template
specializations.  ARM 14.6: "A member function of a template class is
implicitly a template function [...]".  ARM 14.5: "The definition of a
nontemplate function with a type that exactly matches the type of a
function template declarations is a definition of that specific
function template."

I believe all compilers implementing templates allow member function
template specializations, but they differ on how well they handle
specializations when the original template function is inline.  I
believe this applies to non-member function templates as well.

The subject of specializations has been a hot ANSI/ISO topic and
ANSI/ISO has made changes from the ARM.  Stroustrup writes in his new
_The Design and Evolution of C++_ 15.10.3 p375:

 I concluded that specialization as originally defined was a
 botch and also provided essential functinality.  How might we
 provide he functionality while remedying the botch?  After
 many complicated arguments, I proposed a trivially simple
 solution that was accepted at the San Jose meeting: A
 specialization must be declared before it is used.  This
 simply brings specialization into line with the rules of
 ordinary overloading.  If no specialization is in scope at a
 point of use, the general template definition will be used.
 For example:

   template<class T> void sort(vector<T>& v) {/*...*/}

   void sort<char*>(vector<char*>& v);  // specialization

   void f(vector<char*>& v1, vector<String>& v2) {
      sort(v1);  // use specialization
      sort(v2);  // use general template
   }

   void sort<String>(vector<String>& v);// error: specialized after use

   void sort<>(vector<double>& v);// fine: hasn't been used
            ^^-[I guess specifying double is optional, but
         the "<>" are required now for specializations.]

Note that to specialize we must now use the "<>" specialization syntax
instead of the ARM way of providing a regular function definition.
This was done so as not to confuse an explicit declaration (for
purposes of disambiguating template function calls) or overloaded
function with specializations.

D&E doesn't mention how template member functions are now specialized,
but I assume it uses the old syntax since there's no such confusion:

  void Templ1<float>::print() {/*...*/}

I'm not sure how to "declare" this specialization without providing a
definition.  Maybe

  void Templ1<float>::print();  // specialized definition provided in
                                // some .cc file (???)

Jamshid Afshar
jamshid@ses.com