This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [Patch] Document pitfalls of two-stage name lookup
- From: Wolfgang Bangerth <bangerth at ticam dot utexas dot edu>
- To: Gabriel Dos Reis <gdr at integrable-solutions dot net>
- Cc: gcc-patches at gcc dot gnu dot org, <mark at codesourcery dot com>
- Date: Tue, 4 Feb 2003 10:07:10 -0600 (CST)
- Subject: Re: [Patch] Document pitfalls of two-stage name lookup
Gaby,
thanks for your comments.
> s/template-dependent/dependent/g
I added a footnote to explain that dependent=template-dependent and use
the shorter term for the rest of the section. The term "dependent" is
legalise, but may be too non-descriptive for the untrained reader.
Remember that the potential reader of this section is specifically _not_ a
language lawyer :-)
This also applies to this comment:
> | +This sometimes leads to situations with counter-intuitive behavior. The
>
> according to whose intuition?
Given the stream of reports we presently get, I am absolutely sure that
when 3.4 is released we will get more reports from people who claim that
this is counter-intuitive than we have people inside gcc who understand
the matter :-) I used your suggestion anyway.
> For example consider
>
> namespace N {
> template<typename T> T foo(T x) {
> return cos(x);
> }
> }
>
> may fail or succeed.
>
> It fails in
>
> #include <cmath>
> int main() { N::foo(1.0); }
>
> It succeeds in
>
> struct A { };
> A cos(A) { return A(); }
>
> int main() {
> A x;
> N::foo(x);
> }
I don't understand this example. Do you say that the first example fails
if N::foo is declared before the #include? Why? cos(x) is only looked up
at the point of instantiation, which is after the #include. (And does the
fact that you put it into a namespace play any role at all?)
Second draft attached.
W.
-------------------------------------------------------------------------
Wolfgang Bangerth email: bangerth@ticam.utexas.edu
www: http://www.ticam.utexas.edu/~bangerth/
Index: trouble.texi
===================================================================
RCS file: /cvsroot/gcc/gcc/gcc/doc/trouble.texi,v
retrieving revision 1.14
diff -u -r1.14 trouble.texi
--- trouble.texi 24 Jan 2003 15:57:41 -0000 1.14
+++ trouble.texi 4 Feb 2003 16:06:23 -0000
@@ -851,6 +851,7 @@
@menu
* Static Definitions:: Static member declarations are not definitions
+* Templates and base classes:: Accessing members of base classes and templates
* Temporaries:: Temporaries may vanish before you expect
* Copy Assignment:: Copy Assignment operators copy virtual bases twice
@end menu
@@ -891,6 +892,92 @@
does not conform to the standard: @command{g++} reports as undefined
symbols any static data members that lack definitions.
+
+@node Templates and base classes
+@subsection Templates, name lookup, and accessing members of base classes
+
+@cindex base class members
+@cindex two-stage name lookup
+@cindex dependent name lookup
+
+The C++ standard prescribes that all names that are not template
+dependent are bound to their present definitions when parsing a template
+function or class.@footnote{The C++ standard just uses the term
+``dependent'' for names that depend on the type or value of template
+arguments. This shorter term will also be used in the rest of this
+section.} Only names that are dependent are looked up at the
+point of instantiation. For example, consider
+
+@begin example
+ void foo(double);
+
+ struct A {
+ template <typename T>
+ void f () {
+ foo (1); // 1
+ int i = N; // 2
+ T t;
+ t.bar(); // 3
+ foo (t); // 4
+ }
+
+ static const int N;
+ };
+@end example
+
+Here, the names @code{foo} and @code{N} appear in a context that does
+not depend on the type of @code{T}. The compiler will thus require that
+they are defined in the context of use in the template, not only before
+the point of instantiation, and will here use @code{::foo(double)} and
+@code{X::N}, respectively. In particular, it will convert the integer
+value to a @code{double} when passing it to @code{::foo(double)}.
+
+Conversely, @code{bar} and the call to @code{foo} in the fourth marked
+line are used in contexts that do depend on the type of @code{T}, so
+they are only looked up at the point of instantiation, and you can
+provide declarations for them after declaring the template, but before
+instantiating it. In particular, if you instantiate @code{A::f<int>},
+the last line will call an overloaded @code{::foo(int)} if one was
+provided, even if after the declaration of @code{struct A}.
+
+This distinction between lookup of dependent and non-dependent names is
+called two-stage (or dependent) name lookup, and G++ implements it since
+version 3.4.
+
+Two-stage name lookup sometimes leads to situations with behavior
+different from non-template codes. The most common is probably this:
+
+@begin example
+ template <typename T> struct Base {
+ int i;
+ };
+
+ template <typename T> struct Derived : public Base<T> {
+ int get_i() { return i; }
+ };
+@end example
+
+In @code{get_i()}, @code{i} is not used in a dependent context, so the
+compiler will look for a global variable of that name. It will not look
+into the base class, since that is dependent and you may declare
+specializations of @code{Base} even after declaring @code{Derived}, so
+the compiler can't really know what @code{i} would refer to. If there
+is no global variable @code{i}, then you will get an error message.
+
+In order to make it clear that you want the member of the base class,
+you need to defer lookup until instantiation time, at which the base
+class is known. For this, you need to access @code{i} in a dependent
+context, by either using @code{this->i} (remember that @code{this} is of
+type @code{Derived<T>*}, so is obviously dependent), or using
+@code{Base<T>::i}. Alternatively, @code{Base<T>::i} might be brought
+into scope by a @code{using}-declaration.
+
+Note that most compilers get this wrong and accept above code without an
+error. However, this is spurious, since they just don't implement
+two-stage name lookup correctly. This includes G++ versions prior to
+3.4.
+
+
@node Temporaries
@subsection Temporaries May Vanish Before You Expect
@@ -993,7 +1080,7 @@
g++ implements the ``intuitive'' algorithm for copy-assignment: assign all
direct bases, then assign all members. In that algorithm, the virtual
-base subobject can be encountered many times. In the example, copying
+base subobject can be encountered more than once. In the example, copying
proceeds in the following order: @samp{val}, @samp{name} (via
@code{strdup}), @samp{bval}, and @samp{name} again.