D - alias declarations revisited
- Manfred Nowak (141/141) Jan 11 2004 http://www.digitalmars.com/d/declaration.html, [cited 11.01.04]:
- Ben Hinkle (25/36) Jan 11 2004 D is like C++ in this case. Java allows inherited methods to be involved...
- Manfred Nowak (28/29) Jan 12 2004 [...]
- J Anderson (18/48) Jan 12 2004 //That should be
http://www.digitalmars.com/d/declaration.html, [cited 11.01.04]: | class C : B { | int foo( int a ) { return 3; } | alias B.foo foo; | } This looks like a bug to me. At least it fells into the categories `unused variable, unreachable code, x=x' and therefore a maintenance problem. The declaration `class C : B' denotes, that C inherets every function of B by definition. Therefore the alias declaration in this code is at least completely superfluous. The feeling that it should be a bug stems from the fact, that B.foo is a set of functions, namely B.foo and A.foo, which is inhereted into B, and therefore A.foo is reintroduced into the current scope, despite of the fact that it has been just overloaded. But then I read the introductory words again: | Aliases can also 'import' a set of overloaded functions, that can | be overloaded with functions in the current scope This seems to mean, that alias declarations are of lower priority and that the textual order of aliasing/overloading does not matter. If so, then it imposes a recognition problem: after reading some overloading the maintainer eventually reaches an alias declaration that 'imports' overloadable functions. Those functiomns, that are not already overloaded expand the current scope at least temporarily. Therefore a maintainer must recall all already declared functions in the current scope to determine which of the functions introduced by the alias are really expanding the current scope. This holds for every newly found alias declaration. To avoid this the alias declarations must come first, because they are of lower priority, than the declarations in the current scope. But first have a look at the current implementation, when A.foo is overloaded in B: <code> class A { int foo(int a) { return 1; } } class B : A { int foo(int a) { return 4; } // A.foo overloaded int foo( int a, uint b ) { return 2; } } class C : B { int foo( int a ) { return 3; } alias B.foo foo; } class D : C { } void test() { D b = new D(); int i; i = b.foo(1, 2u); // calls B.foo printf("%d\n",i); } void main() { test(); } </code> The code compiles and the printing consists of the expected number `2'. But oops, the call of `i = b.foo(1);' was lost. Reintroducing it: <code> [...] void test() { D b = new D(); int i; i = b.foo(1, 2u); // calls B.foo i = b.foo(1); // lost code reintroduced here printf("%d\n",i); } [...] </code> yields the compiler error: | test.d(25): function foo overloads int(int a) and int(int a) both | match argumentlist for foo This is inacceptable. Usually the class designers are not identical with the users of the class. Therefore this behaviour of the compiler is equivalent to a qualitiy control by the consumer. And in addition, there should be no error message at all, because the questionable function was overloaded in the current 'importing' scope. Now lets change the textual order of the alias and the overloading. <code> [...] class C : B { alias B.foo foo; int foo( int a ) { return 3; } } [...]</code> This yields the compiler error: | test.d(6): function foo conflicts with C.foo at test.d(12) B.foo(int) conflicts with its overloading function C.foo(int)? According to the introductuary words this should not happen at all, but the error message is presented before the actual call of the consumer and that is clearly better. Conclusion: the actual behaviour of the compiler is inconsistent with the supposed meaning of the documentation. IMO the supposed meaning of the documentation should guide the implementation. Moreover, if the supposed meaning takes the lead, then D would have a nice method of contructing new classes. Example: <code> class A1 { int foo(int a){return 1;}; } class A2 : A1 { int foo(int a, uint b){return 2;} } class B1 { uint bar(int a){return 3;}; } class B2 : B1 { int bar(int a, uint b){return 4;} } class C { alias A2.foo xxx; alias B2.bar xxx; //A2.foo conflicts with B2.bar int xxx(int a, uint b){return 5;} //conflict resolved by overloading } //C contains A1.foo, B1.bar, C.xxx class D : C { } void test() { D b = new D(); int i; i = b.xxx(1, 2u); // calls C.xxx printf("%d\n",i); } void main() { test(); } </code> But currently the compiler message is: | test.d(14): function bar conflicts with C.xxx at test.d(21) I.e. B2.bar conflicts with C.xxx. And when omitting the overloading the underlying conflict is only detected when a consumer tries to call the conflictuary function. :-( So long. -- Fight Spam! Join EuroCAUCE: http://www.euro.cauce.org/ 2EA56D6D4DC41ABA311615946D3248A1
Jan 11 2004
"Manfred Nowak" <svv1999 hotmail.com> wrote in message news:btr852$2d1q$1 digitaldaemon.com...http://www.digitalmars.com/d/declaration.html, [cited 11.01.04]: | class C : B { | int foo( int a ) { return 3; } | alias B.foo foo; | } This looks like a bug to me. At least it fells into the categories `unused variable, unreachable code, x=x' and therefore a maintenance problem. The declaration `class C : B' denotes, that C inherets every function of B by definition. Therefore the alias declaration in this code is at least completely superfluous.D is like C++ in this case. Java allows inherited methods to be involved in overload resolution but C++ doesn't. I think the reason C++ doesn't is that overloading can be surprising when combined with implicit type conversions, causing maintenance problems of its own. Example: class B { void foo(short a) {...} } class C : B { void foo(int a) {...} void bar() { short x=2; foo(x); } } Unless you go poking around in B you'd think that bar would call C.foo but if inherited methods took part in overloading then it would call B.foo. When you explicitly use an alias to bring in the other definition of foo then someone reading C will know to go look in B for more foo definitions. This example seems pretty trivial but imagine if there were 4 or 5 layers of inheritance between B and C. Then again, I'm with you that D should use the Java model - overload with abandon. :-) -Ben
Jan 11 2004
Ben Hinkle wrote: [...]causing maintenance problems of its own. Example:[...] What is wrong with dmd? Trying to test your code completed with a main() function: <code> class B { void foo(short a) {} } class C { void foo(int a) {} void bar() { short x=2; foo(x); } } void main() { C c; c.bar(); } </code> It compiles but produces a runtime-error: Access Violation So long. -- Fight Spam! Join EuroCAUCE: http://www.euro.cauce.org/ 2EA56D6D4DC41ABA311615946D3248A1
Jan 12 2004
Manfred Nowak wrote:Ben Hinkle wrote: [...]//That should be class B { void foo(short a) {} } class C { void foo(int a) {} void bar() { short x=2; foo(x); } } void main() { C c = new C; c.bar(); }causing maintenance problems of its own. Example:[...] What is wrong with dmd? Trying to test your code completed with a main() function: <code> class B { void foo(short a) {} } class C { void foo(int a) {} void bar() { short x=2; foo(x); } } void main() { C c; c.bar(); } </code> It compiles but produces a runtime-error: Access Violation So long.
Jan 12 2004