[...]
Name lookup rules are sometimes quite strict and the compiler
vendors feel like they are doing us a favour if they let us
write code like that.
More importantly, name lookup rules changed with regards to
previously existing practice, and most compiler vendors feel
like they are doing you a favor not breaking existing code.
(I'm pretty sure that EDG has an option to allow this to
compile, even today, and you don't get any more standard
compliant than they.)
FWIW: strictly speaking, name lookup rules didn't change,
because there weren't any templates in C++ before the committee
added them. In practice, the issue is rather cloudy with
regards to who did what: the original CFront implementation of
templates (around 1990) didn't use two phase lookup. The
original CFront implementation of templates was also considered,
by it's authors, as very experimental (i.e. subject to change).
As early as 1992 or 1993, I think, it was clear that the final
standard would have something along the lines of two phase
lookup. Still, many compiler vendors implemented templates more
or less as "extended macros", without any warning to their
customers that this feature was unstable, even in its
definition, and shouldn't be used in production code. And to be
fair, given the alternatives (e.g. <generic.h>), most
programmers would have jumped on the possibility of using these
templates even had they known just how much they were likely to
change. Finally, it took longer than expected to finalize the
standard, and even longer for compilers to begin to be
conformant, so we end up with a large body of code which is,
according to the standard, broken. Compiler vendors (correctly,
IMNO) want to make life as easy as possible for their existing
customers, so they avoid breaking working code whenever
possible, even if it means non-conformance.
You might want to try your compilers
with this:
template<class T> struct B {};
template<class T> struct A : B<T>
{
static int foo() { return GlobalVar; } // line 4
};
int main() {
return A<char>::foo();
}
If they compile this, they are in violation of the rule set forth
in Standard, [temp.res]/9. I tried it with Comeau online, it did
not compile it ('GlobalVar' is undefined on line 4). VC++ 2005
compiled it. VC++ 2008 EE compiled it.
In all three cases, with what options? From a quality of
implementation point of view, any modern compiler should be able
to work both ways: rejecting it in new code, but accepting in
legacy code. And of course, the only way the compiler knows
what code is new, and what is legacy, is that you tell it, by
means of an option. (Given the goals of the Comeau on line
compiler, Greg may not have activated this option in the EDG
front-end. Using it there wouldn't make sense. But I'm sure
it's there in the front-end, and I would hope that he's provided
an option to activate it in the versions he sells.)