
On Tue, Feb 17, 2004 at 12:29:25PM +0100, Gabriel Dos Reis wrote:
Daniel Frey <daniel.frey@aixigo.de> writes: | Apologies in advance if I jump in too quickly without fully | understanding your point, but to me it seems that the current rule is | consistent only when viewed from a certain point of view.
Someone has characterized it as inconsistent without giving details as to why; all I'm saying is that, if one accepts the principle that "scopes nest", then the rule is a logical consequence, hence consistent with that principle. Now, it might be that that logical consequence is unacceptable for some non-rational reasons, but that does not mean the consequence itself is inconsistent.
| The POV is that | | class B { typename X }; | | template< typename X > class Y // 1 | : public B // 2 | { // 3 | ... | }; | | You think that template parameter X is declared in line 1, then hidden | by the base classes name introduced by line 2, thus non-accessible in | line 3, right? You think of the name scopes to nest in the same order, | do you?
What I think is: If we accept the principle that scopes should nest, then we should accept its logical consequences; if don't want to accept those logical consequences, then we should abandon the idea that scopes nest. Which one do you want to pick?
I do find this (Gaby's) argument quite compelling. Nevertheless, despite its logic, human psychology seems resistant to it. I have been thinking about it a lot, and I think I finally nailed down why. Here's my story. Most scopes nest. Most of the time, introducing a new scope and new names happens in a way that all of the "new" names are visible at the point of introduction: int x; double y; void f( Foo x, Bar z ) { // x and z introduced here, _explicitly_ // x is a Foo, y a double, z a Bar } As homo sapiens we seem good at coping with this kind of nesting/shadowing, owing to the explicitness. However there are two kinds of language constructs which introduce names in an "invisible" way: using namespace foo; // many new names come in from foo struct Derived : Base { // many new names come in from Base These constructs are the potentially problematic ones from a psychological point of view. Now, for whatever reason, C++ has a "good" rule with regards to namespaces bringing in hosts of new names all at one. Witness: -------------------------------------------------- namespace Foo { void f() { cout << "1" << endl; } } void f() { cout << "2" << endl; } using namespace Foo; int main() { f(); } -------------------------------------------------- The compiler warns of an ambiguity. Saying "using namespace foo" does not shadow outer names, but rather brings them into equal contention in the current scope. The ambiguity diagnostics are good from the human perspective; these non-visible-at-the-point-of-introduction names are the ones we are apt to forget about. However inheritance is error-prone: -------------------------------------------------- struct Base { void f() { cout << "1" << endl; } }; ... // maybe many many lines of code here void f() { cout << "2" << endl; } struct Derived : Base { void g() { f(); } }; int main() { Derived().g(); } -------------------------------------------------- He who reads the code for Derived is apt to be mistaken about the f() being called. Unlike namespaces, inheritance introduces "invisible" new names in a way in which they hide/shadow the old names, rather than merely coming into equal contention (and causing an ambiguity error requiring the programmer to explicitly specify what is desired). The "moral" I am trying to sell is that this issue has nothing to do with templates. It is "inheritance" which is the error-prone construct here. If the rules for inheritance worked like the rules for "using namespace", then I think this "intuitive naming issue" would be solved. That said, I have not given any thought/consideration to what the implications would be for changing the way "inherited names" work in the language. Offhand it seems like currently legal programs would either stay legal with the same semantics, or become non-well-formed (compiler diagnostic). But I don't want to put the cart ahead of the horse yet. Does anyone agree with my "moral"? Let's stick with that question first. -- -Brian McNamara (lorgon@cc.gatech.edu)