$ cat junk.cc template <typename T> struct List { struct D { int size; }; D *d; List &fill(const T &t, int size = -1); }; template <typename T> List<T> &List<T>::fill(const T &t, int size) { resize(size ? 1 : d->size); return *this; } $ /usr/local/gcc-4.1-20051001/bin/g++ -c junk.cc # silently accepted Replacing "resize(size...);" with "resize(1);" correctly rejects the bogus source (g++ versions 3.4 and above): junk.cc: In member function 'List<T>& List<T>::fill(const T&, int)': junk.cc:13: error: there are no arguments to 'resize' that depend on a template parameter, so a declaration of 'resize' must be available junk.cc:13: error: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated)
Hmm, GCC seems to think "size ? 1 : d->size" is type-dependent in a way it is as D is type dependent. Someone else with more knowlege of the C++ standard will have to comment. Another testcase which shows the same issue template <typename T> struct List { struct D { int size; }; D *d; List &fill(const T &t, int size = -1); }; template <typename T> List<T> &List<T>::fill(const T &t, int size) { resize(d->size); return *this; } ----------------------- Comeau says this is invalid code but I don't know really.
(In reply to comment #1) > Hmm, GCC seems to think "size ? 1 : d->size" is type-dependent in a way it is > as D is type dependent. I disagree. D is not type depenendent at all -- it is a struct containing exactly one int field; it doesn't change in any way no matter what instantiation args are used. Therefore, d->size is not type dependent either.
Another variation of the same theme: template <typename T> List<T> &List<T>::fill(const T &t, int size) { this->resize(1); this->resize(d->size); return *this; } $ /usr/local/gcc-4.1/bin/g++ -c junk.cc # silently accepted $ edgcpfe --gnu_version=30400 junk.cc "junk.cc", line 13: error: class template "List<T>" has no member "resize" this->resize(1); ^ "junk.cc", line 14: error: class template "List<T>" has no member "resize" this->resize(d->size); ^ 2 errors detected in the compilation of "junk.cc". # same errors with 'edgcpfe --strict' BTW, the test case is reduced from Qt4 sources, where 'resize()' is a typo: reserve() was intended. IOW, g++ is hiding the bug from Qt developers.
See 14.6.2.1 "Dependent names" and 14.6.3 "Non Dependent name"
this better be type dependent, otherwise it is just useless really.
Just to be clear: List<T> *is* type dependent. List<T>::D is *not*.
List<T>::D is by 14.6.2.1 pargagraph 1 point 2: a qufified-id with a nested-name-specifier which contains a class-name that names a dependent type of whos unqualified-if names a dependent type. So by those rules it is type dependent. (maybe I am missing something).
But 14.6.2.1 says: "Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters." 14.6.2.2 [Example template <class T> struct X : B<T> { typename T::A* pa; void f(B<T>* pb) { static int i = B<T>::i; pb->j++; } }; the base class name B<T>, the type name T::A, the names B<T>::i and pb->j explicitly depend on the _template-parameter_. -- end example] There is no explicit dependence for List<T>::D on the template parameter. The semantics of D do not change depending upon the instantiation of T. I believe point two refers to this kind of construct: List::D<T> "-- a _qualified-id_ with a _nested-name-specifier_ which contains a class-name that names a dependent type...." IOW: if the nested name depends on the template arg, then the nesting entity does too. But not the other way around.
(In reply to comment #8) Hmm, I assume you are looking at C++03 and not C++98. I was looking at C++98, maybe there was some defect report about this.
(In reply to comment #9) Never mind that, the numbers have changed a little. Take the following example: template <typename T> struct List { struct D { int size; }; D *d; List &fill(const T &t, int size = -1); }; template<> struct List<int>::D { int d; }; template <typename T> List<T> &List<T>::fill(const T &t, int size) { resize(d->size); return *this; } int main(void) { List<int> a; a.fill(1, 1); } This is really invalid code as List<int>::D::size does not exist but it can only be diagnost at instaination time.
So it looks like we have found an EDG bug and not a GCC one. As for >There is no explicit dependence for List<T>::D on the template parameter. >The semantics of D do not change depending upon the instantiation of T. That is false see my example.
(In reply to comment #10) > This is really invalid code as List<int>::D::size does not exist but it can > only be diagnost at instaination time. Oh and resize is not declared either but since d->size is dependent as shown by my example it cannot be looked up at defintion time.
Ok, here is a real testcase which shows that EDG gets it really wrong: #include <stdio.h> void resize(double&) { printf("resize::double\n"); } void resize(int&) { printf("resize::int\n"); } template <typename T> struct List { struct D { int size; }; D *d; List &fill(const T &t, int size = -1); }; template<> struct List<int>::D { double size; }; template <typename T> List<T> &List<T>::fill(const T &t, int size) { resize(d->size); return *this; } int main(void) { List<int> a; a.fill(1, 1); return 0; } ------- So you found a bug in EDG and not GCC. The error which EDG front-end gives: "ComeauTest.c", line 33: error: a reference of type "int &" (not const-qualified) cannot be initialized with a value of type "double" resize(d->size); ^ detected during instantiation of "List<T> &List<T>::fill(const T &, int) [with T=int]" 1 error detected in the compilation of "ComeauTest.c". Which does not make sense at all since List<T>::D is type dependent and so is d->size as shown above.
(In reply to comment #13) > The error which EDG front-end gives: > "ComeauTest.c", line 33: error: a reference of type "int &" (not > const-qualified) > cannot be initialized with a value of type "double" > resize(d->size); > ^ > detected during instantiation of > "List<T> &List<T>::fill(const T &, int) [with T=int]" > 1 error detected in the compilation of "ComeauTest.c". The other reason why this does not make sense is that EDG/Comeau is saying that the type of d->size is always int but when instantiate List<T>::fill, the front-end sees that it is not an int and then errors out.
hold one for a second there is a defect report about this: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#224 See also: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1251.html So EDG just does not follow the defect report.
Just for later reference the defintion of what a dependent name is slightly different now after the DR: A name is a member of an unknown specialization if the name is a qualified-id in which the nested-name-specifier names a dependent type that is not the current instantiation. A type is dependent if it is a template parameter, a member of an unknown specialization, a nested class that is a member of the current instantiation, a cv-qualified type where the cv-unqualified type is dependent, a compound type constructed from any dependent type, an array type constructed from any dependent type or whose size is specified by a constant expression that is value-dependent, or a template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent. ----- This is obvious the "a nested class that is a member of the current instantiation" case.
I concede the argument.