Topic: Template name resolution


Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/09/25
Raw View
On Fri, 19 Sep 1997 07:25:40 GMT, dHarrison@worldnet.att.net (Doug
Harrison) wrote:

>The Dec-96 DWP, sec. 14.6.2, says:
>
>"4 In the definition of a class template or in the definition of a
>member of such template that appears outside of the template
>definition, if a base class of this template depends on a
>template-argument,  the  base class  scope  is not examined during
>name look up until the class template is instantiated.  [Example:
>
>          typedef double A;
>          template<class T> B {
>                  typedef int A;
>          };
>          template<class T> struct X : B<T> {
>                  A a;
>          };
>  X<T>::a has type double.  The type name A binds to  the  typedef
>name defined in the global namespace scope, not to the typedef name
>defined in the base class B<T>."
>
>Presumably, one could get B<T>::A by writing "typename B<T>::A a;" and
>if this is correct, I have slightly less of a problem with the
>example, though it seems very surprising that a name from an outer
>scope is taking precedence over a name from an inner scope. Both
>compilers I have (Visual C++ 5 and gcc 2.7.2) interpret 'A' as int in
>the above example, not double, as the DWP requires, but such
>discrepancies are hardly surprising.
>
>Now, suppose I remove the global typedef. As used by X, A is not
>dependent, so is bound at the point of template definition, and since
>the base class isn't searched, there's nothing to bind it to. Thus,
>the fragment becomes ill-formed, right?
>
>I also wonder what the implication are for functions. Suppose B
>defines a function "void fun1();", and a function X::fun2 calls it as
>"fun1();". As I understand things, fun1 is not dependent (14.6.2.1),
>so it's bound at the point of use (14.6.3), in the template definition
>of X. Since dependent base classes aren't searched until instantiation
>(14.6.2.4), there's nothing to bind fun1 to. Is the result ill-formed?
>The second example in 14.6.2.2.3 seems to imply that it is. Is it
>necessary to call such functions as (for example) "this->fun1()"?
>
>Comments? I'd appreciate learning if I've interpreted the draft
>correctly, and whether there are other related "gotchas". As described
>above, 14.6.2.4 breaks an interpretation common to at least two
>compilers, and I'd appreciate learning the motivation behind the
>changes.

Since writing the above, I've read the relevant section in D&E,
15.10.2.2, which uses an example like the following:

 void g() {}

 template<typename T>
 struct A : T
 {
    void f() { g(); }
 };

The argument is that the template author most likely expects to call
::g(), so the binding occurs at definition time, rather than at the
point of instantiation. I can appreciate this argument. However, I
don't believe it extends to the example in the DWP 14.6.2.4, which
I've made analogous to the D&E example:

 void g() {}

 template<typename T>
 struct B
 {
    void g() {}
 };

 template<typename T>
 struct X : B<T>
 {
    void f() { g(); }
 };

I think it's wrong for X<T>::f() to call ::g(), instead of B<T>::g(),
but the draft appears to require exactly that. The D&E presents an
example of a derivation for which nothing is known about the base
class, since it is a template parameter. The DWP example concerns a
derivation for which the set of base class member names is known with
certainty, irrespective of the template parameter T. As a result, the
author of X<T>::f() will naturally expect his unqualified call of g()
to resolve to B<T>::g(). After all, he can look into the definition of
B<T> and see all its members, just as if B was a normal class, instead
of a class template.

I became interested in this issue after reading this message, which
unfortunately failed to inspire a definitive response:

Newsgroups: comp.lang.c++.moderated,comp.sys.hp.hpux
Subject: Global scope, base class scope, and templates
Date: 9 Sep 1997 21:13:59 -0400
Message-ID: <5v4s8f$gf8@netlab.cs.rpi.edu>

The poster described a scenario just like the one immediately above,
in which the global function "terminate" was called instead of the
base class version, resulting in his program exiting. He believed it
to be a compiler bug, but after reading the DWP, I'd say it's a
requirement of the DWP, and his compiler is correct, going by the DWP.
I have a fair amount of code myself that would be broken by this
interpretation of the draft. I'd be relieved to hear my interpretation
is wrong, and if it is, I'd appreciate learning why; however, if it's
correct, then I think it demonstrates a serious flaw in this tentative
definition of C++. The rule described by the Dec-96 DWP, section
14.6.2.4, seems overly general and wrong. Comments?

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/09/19
Raw View
The Dec-96 DWP, sec. 14.6.2, says:

"4 In the definition of a class template or in the definition of a
member of such template that appears outside of the template
definition, if a base class of this template depends on a
template-argument,  the  base class  scope  is not examined during
name look up until the class template is instantiated.  [Example:

          typedef double A;
          template<class T> B {
                  typedef int A;
          };
          template<class T> struct X : B<T> {
                  A a;
          };
  X<T>::a has type double.  The type name A binds to  the  typedef
name defined in the global namespace scope, not to the typedef name
defined in the base class B<T>."

Presumably, one could get B<T>::A by writing "typename B<T>::A a;" and
if this is correct, I have slightly less of a problem with the
example, though it seems very surprising that a name from an outer
scope is taking precedence over a name from an inner scope. Both
compilers I have (Visual C++ 5 and gcc 2.7.2) interpret 'A' as int in
the above example, not double, as the DWP requires, but such
discrepancies are hardly surprising.

Now, suppose I remove the global typedef. As used by X, A is not
dependent, so is bound at the point of template definition, and since
the base class isn't searched, there's nothing to bind it to. Thus,
the fragment becomes ill-formed, right?

I also wonder what the implication are for functions. Suppose B
defines a function "void fun1();", and a function X::fun2 calls it as
"fun1();". As I understand things, fun1 is not dependent (14.6.2.1),
so it's bound at the point of use (14.6.3), in the template definition
of X. Since dependent base classes aren't searched until instantiation
(14.6.2.4), there's nothing to bind fun1 to. Is the result ill-formed?
The second example in 14.6.2.2.3 seems to imply that it is. Is it
necessary to call such functions as (for example) "this->fun1()"?

Comments? I'd appreciate learning if I've interpreted the draft
correctly, and whether there are other related "gotchas". As described
above, 14.6.2.4 breaks an interpretation common to at least two
compilers, and I'd appreciate learning the motivation behind the
changes.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]