Topic: bug in cfront3.0.1 ??


Author: jamshid@ut-emx.uucp (Jamshid Afshar)
Date: 2 Nov 92 23:02:26 GMT
Raw View
In article <Bww4zK.2z8@apollo.hp.com> vinoski@apollo.hp.com (Stephen Vinoski) writes:
>In article <22252@sbsvax.cs.uni-sb.de> stefan@mpi-sb.mpg.de writes:
>>Is this a bug of cfront3.0.1 ???
>>template <class T>
>>void Print(T x, ostream& out)   { x.print(out); }   // (*)
>>
>>void Print(int x, ostream& out) { out << x; }       // (**)
>>
>>main()
>>{
>>  int y=0;
>>  Print(y,cout);   // cfront tries to instantiate template (*)
>>                   // I think it should call the non-template variant (**)
>>}
>
>If you change the signature of the Print template specialization to
> void Print(int, ostream_withassign&);
>your code will work as expected.

I think you misunderstood the original question.  Stefan does *not*
want the template function to be instantiated (because it expects 'T'
to be a class with a Print(ostream&) member function).  He wants the
"specific template function" (ARM 14.5c)
 void Print(int,ostream&);
that he defined to be used instead.  I imagine cfront would correctly
call Print(int,ostream&), but as a side-effect it first instantiates
the Print template function and this causes a syntax error.  ARM 14.5c
says:
 If the definition of a specific template function or specific
 template class is needed to perform some operation and if no
 explicit definition of that specific template function or
 class is found in the program, a definition is generated.

While I don't think this area of the language is fully specified yet,
I would expect an implementation to not attempt to parse a template
function definition if a specific definition is provided.

This is part of the problem of specifying the conditions under which a
template function can be parsed.  For example, I would like to defined
an operator<<(ostream&, const List<T>&) function for my List class,
but if a user instantiates List with a T that does not define an
output operator, I don't think that user should get a syntax error
unless they actually try to output a list.  What do cfront and gcc do
in this situation?  Borland C++ 3.1 only instatiates a template
function (and therefore would only give a syntax error) if it is a
member function or 'friend' function of an instantiated class.
Therefore, I currently workaround this problem by not making
operator<<(ostream&, const List<T>&) a 'friend' of List<T>.  But, what
if it needs to be 'friend' or if I wanted to define a
List<T>::print(ostream&) member function instead?  Should not even
member functions be parsed unless they are called?

>The problem has to do with exact matching of argument types required
>by the ARM for template functions.  Section 14.4, page 346 of the ARM
>states:
>
>"A match on a template...implies that a specific template function
>with arguments that exactly match the types of the arguments will be
>generated.  Not even trivial conversions will be applied in this
>case."

Cfront does not follow this overly strict rule according to Lippman's
2ndEd (end of 4.2).  It performs trivial conversions (eg, T->const T,
T[]->T*) and standard conversions (eg, Derived* -> Base*).  Lippman
doesn't say, but does cfront also perform promotions (eg,
float->double, char->int) when matching template functions?  It seems
like it should since a match with promotions is better than a match
with standard conversions (ARM 13.2).  Borland C++ 3.1 unfortunately
seems to follow the strict ARM 14.4 rule.  I'm not sure what gcc does
(anybody know?).  Is ANSI leaning in any particular direction, or is
this still being debated?

This article has been crossposted to, and followups are redirected to,
comp.std.c++.

Jamshid Afshar
jamshid@emx.utexas.edu




Author: vinoski@apollo.hp.com (Stephen Vinoski)
Date: Tue, 3 Nov 1992 05:20:59 GMT
Raw View
In article <82902@ut-emx.uucp> jamshid@emx.utexas.edu writes:
>In article <Bww4zK.2z8@apollo.hp.com> vinoski@apollo.hp.com (Stephen Vinoski) writes:
>>In article <22252@sbsvax.cs.uni-sb.de> stefan@mpi-sb.mpg.de writes:
>>>Is this a bug of cfront3.0.1 ???
>>>template <class T>
>>>void Print(T x, ostream& out)   { x.print(out); }   // (*)
>>>
>>>void Print(int x, ostream& out) { out << x; }       // (**)
>>>
>>>main()
>>>{
>>>  int y=0;
>>>  Print(y,cout);   // cfront tries to instantiate template (*)
>>>                   // I think it should call the non-template variant (**)
>>>}
>>
>>If you change the signature of the Print template specialization to
>> void Print(int, ostream_withassign&);
>>your code will work as expected.
>
>I think you misunderstood the original question.  Stefan does *not*
>want the template function to be instantiated (because it expects 'T'
>to be a class with a Print(ostream&) member function).  He wants the
>"specific template function" (ARM 14.5c)
> void Print(int,ostream&);
>that he defined to be used instead.  I imagine cfront would correctly
>call Print(int,ostream&), but as a side-effect it first instantiates
>the Print template function and this causes a syntax error.  ARM 14.5c
>says:
> If the definition of a specific template function or specific
> template class is needed to perform some operation and if no
> explicit definition of that specific template function or
> class is found in the program, a definition is generated.

I thought I understood his question perfectly; perhaps my response
wasn't clear enough.  Let me try again.

Both of my suggestions result in the template specialization being
called, with no template instantiation required.  If an instantiation
were called instead of the specialization, an error would result
because type 'int' has no 'print' member function (as you correctly
point out).

By changing the second parameter of the Print function from type
ostream& to ostream_withassign&, or by passing an ostream& instead of
an ostream_withassign& as the second parameter, the call to Print in
main exactly matches the specialization.  As it is, the template
instantiation system doesn't think that the call to Print matches the
specialization, resulting in an attempted instantiation of the
function template.  This is an apparent contradiction to Lippman page
494, but it is caused by a limitation of cfront 3.0 (see below).

While not a definitive answer for all compilers, I verified this
behavior using HP C++ A.03.00, which is derived from USL cfront 3.0.1,
the very compiler Stefan is wondering about.


>While I don't think this area of the language is fully specified yet,
>I would expect an implementation to not attempt to parse a template
>function definition if a specific definition is provided.

This is mentioned in the annotations in section 14.5 on page 347 of
the ARM, and this is how cfront 3.0 behaves.


>>The problem has to do with exact matching of argument types required
>>by the ARM for template functions.  Section 14.4, page 346 of the ARM
>>states:
>>
>>"A match on a template...implies that a specific template function
>>with arguments that exactly match the types of the arguments will be
>>generated.  Not even trivial conversions will be applied in this
>>case."
>
>Cfront does not follow this overly strict rule according to Lippman's
>2ndEd (end of 4.2).  It performs trivial conversions (eg, T->const T,
>T[]->T*) and standard conversions (eg, Derived* -> Base*).

Yes, I know; my original response referred to page 494 of Stan's text.
However, according to my experiments and seemingly contrary to what
Stan says, no standard conversion of ostream_withassign& to ostream&
is applied for Stefan's example code.

The reason is as clear as mud: according to the C++ 3.0 Release Notes,
instantiation for a single file program occurs directly into the
object file rather than into a template repository (section 8, page
21).  Since Stefan's example is a single file program, the repository
is not used, and therefore no "pre-linking" occurs.  The instantiation
system therefore does not know that a template specialization exists,
and so tries to instantiate the function template.  It of course fails
due to compilation errors.

Given this behavior, let me suggest a third way to fix this problem:
separate compilation.  By moving the function template declaration and
definition into a separate include file and implementation file,
respectively, it all works.  This is because the separate compilation
forces the use of a template repository, resulting in a "pre-link"
phase that picks up the existence of the specialization and prevents
instantiation of the function template.  A fourth fix is to leave
everything in one file, but to explicitly separate the compilation and
link phases.  In other words, first compile using -c, then link.  No
repository exists in this case, so no "pre-link" is performed, but it
isn't necessary because the template specialization resolves the
reference.

These fixes proves that Lippman is indeed correct in claiming that USL
C++ 3.0 performs standard conversions when resolving references to
overloaded functions (did anyone really doubt that he was wrong? :-)).
The behavior Stefan observed is basically a limitation of cfront 3.0.

Hopefully the ANSI C++ Committee will clear up all of this confusion.

-steve
--
Steve Vinoski  (508)436-5904   vinoski@apollo.hp.com
Distributed Object Computing Program
Hewlett-Packard, Chelmsford, MA 01824       These are my opinions.