August 02, 2007
Derek Parnell wrote:
> On Wed, 01 Aug 2007 17:37:58 -0400, Steve Schveighoffer wrote:
>> This is very counterintuitive...
> 
>> My point basically is why should a coder be forced to declare
>> an alias when realistically there is no reason they would
>> NOT want to declare the alias?
> 
> Oh don't get me wrong, I too think that the current rules are totally daft.
> I mean, why does one go to the trouble of deriving one class from another
> if its not to take advantage of members in the parent class?
> 
> I vaguely recall Walter saying that he decided to do it this way because it
> is easy to implement this rule into the compiler and it makes the compiler
> run faster. (I am paraphrasing from memory, so I could be totally wrong).
> 
> If this is so, then it strikes me that the language is this way in order to
> make life easier for compiler writers than for D coders, which is just not
> right in my POV.

You're not recalling Walter reasoning correctly. :)

The reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has.  He also explained why C++ has that behaviour, I've linked his original arguments at the end of this post.

#include <stdio.h>

class Base
{
public:
	void foo()
	{
		printf("Base::foo()\n");
	}

	void foo(int i)
	{
		printf("Base::foo(i)\n");
	}
};

class Child : Base
{
public:
	void foo(int i)
	{
		printf("Child::foo(i)\n");
	}
};

void main()
{
	Child c;
	c.foo();
}

Some references to Walters original comments on the topic.  Anyone who is interested should probably read this entire thread (though it is a monster) to get a really clear picture of the problems associated with this:

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=6975
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7053
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7063
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7137
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7149
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7057
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7072

I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues.

Regan
August 02, 2007
Regan Heath pisze:

> #include <stdio.h>
> 
> class Base
> {
> public:
>     void foo()
>     {
>         printf("Base::foo()\n");
>     }
> 
>     void foo(int i)
>     {
>         printf("Base::foo(i)\n");
>     }
> };
> 
> class Child : Base
> {
> public:
>     void foo(int i)
>     {
>         printf("Child::foo(i)\n");
>     }
> };
> 
> void main()
> {
>     Child c;
>     c.foo();
> }
> 
> Some references to Walters original comments on the topic.  Anyone who is interested should probably read this entire thread (though it is a monster) to get a really clear picture of the problems associated with this:
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=6975 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7053 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7063 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7137 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7149 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7057 
> 
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=7072 
> 
> 
> I could keep linking all day... these are all from the same thread so starting at the top and reading the whole thread should give you a fair idea of the issues.
> 
> Regan

ups... my argument from last post falled down into ruins...

I am little bit shocked that C++ does it same way as D... As I did not yet read all mentioned threads, currently I am not telling any word more :-)

Regards
Marcin Kuszczak
(Aarti_pl)
August 02, 2007
On Thu, 02 Aug 2007 09:32:31 +0100, Regan Heath wrote:

> You're not recalling Walter reasoning correctly. :)

Thanks Regan. I'd succeded in burning that discussion out of my mind :)

> The reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has.  He also explained why C++ has that behaviour, ...

Ok, I re-read the threads again and still draw the same conclusion. Walter has got this one wrong. D should be better than C++, IMHO.

-- 
Derek Parnell
Melbourne, Australia
skype: derek.j.parnell
August 02, 2007
Derek Parnell wrote:
>> The reason it is done the way it is, the reason Walter gave, is that this is the same behaviour C++ has.  He also explained why C++ has that behaviour, ...
> 
> Ok, I re-read the threads again and still draw the same conclusion. Walter
> has got this one wrong. D should be better than C++, IMHO.

I'm not sure it's as simple as 'better' or worse.  I think it's another case where D makes a tradeoff and as always happens when you make a tradeoff some people would have 'gone the other way'.

Doing it the C++ way prevents the problems Walter describes but it requires you to manually 'alias' symbols into the derived class.

Doing it the Java way (as many people seem to expect, more intuitive?) doesn't require you to 'alias' the symbols into the derived class but you are vulnerable to the problems Walter describes (I believe this was prooven in that old thread).

So the tradeoff is safety/explicit/control (C++ way) vs brevity/intuitiveness (Java way).

I would have thought that given your personal preference for being explicit in many cases you would prefer the C++ approach?

(I'm not trying to paint you into a corner with this statement, you're perfectly entitled to have a different opinion for this case as no two cases are identical)

Further thoughts.. (arguing both sides)

Steve gave an example in this very thread where the C++ behaviour can cause errors for users/consumers of the classes so that would count as a penalty for the C++ case.  Conversely you could argue that at least this isn't a silent error as the problems Walter describe are (from memory - pls correct me if I haven't remember it correctly).

You could argue D with it's single inheritance and interfaces is closer to Java than C++.  In a situation where you have multiple inheritance it's certainly more likely to be complex in which case the C++ approach makes it easier to understand and therefore use derived classes correctly.  Conversely you could argue any inheritance is complex enough and justifies the C++ behaviour.

Regan
August 02, 2007
Regan Heath Wrote:

> Steve gave an example in this very thread where the C++ behaviour can cause errors for users/consumers of the classes so that would count as a penalty for the C++ case.  Conversely you could argue that at least this isn't a silent error as the problems Walter describe are (from memory - pls correct me if I haven't remember it correctly).

OK, I was totally under the impression that C++ was the way I was saying D should be.  I was assuming that C++ got it right.  More than I can't believe that C++ is not right, I'm surprised I never had problems with it...

However, I think it should be changed.  Not sure if it will, as it seems there  is already a precedent.  In my opinion, the base class should be examined if the derived class does not provide a suitable match.  I can't see how this would cause too much of a performance hit, especially since the choice of the compiler at that point is to error out (current implementation) or continue searching (my proposition).  If the derived class can provide a suitable method, then it should be no more performance hit than the current implementation.

BTW, my example comes from a real world case... tango :)  If you look at tango.io.selector.EPollSelector it only implements select(float).  It is trying to rely on the AbstractSelector's select() to call the other version, but the author failed to include the alias statement.  Note that this essentially makes it seem like EPollSelector DOES NOT implement the ISelector interface, since ISelector calls for a select() method.  That is, unless you cast to an AbstractSelector or ISelector, which is my workaround.

-Steve
August 02, 2007
Steve Schveighoffer wrote:
> Regan Heath Wrote:
> 
>> Steve gave an example in this very thread where the C++ behaviour
>> can cause errors for users/consumers of the classes so that would
>> count as a penalty for the C++ case.  Conversely you could argue
>> that at least this isn't a silent error as the problems Walter
>> describe are (from memory - pls correct me if I haven't remember it
>> correctly).
> 
> OK, I was totally under the impression that C++ was the way I was
> saying D should be.  

I suspect most people think C++ does it the way you suggested.

> I was assuming that C++ got it right.  More than
> I can't believe that C++ is not right, I'm surprised I never had
> problems with it...

It's truly odd that no-one seems to have a problem with it in C++.  It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class.  Perhaps everyone just does that without thinking about it.

> However, I think it should be changed.  Not sure if it will, as it
> seems there  is already a precedent.  In my opinion, the base class
> should be examined if the derived class does not provide a suitable
> match.  I can't see how this would cause too much of a performance
> hit

Performance isn't the reason against the Java behaviour, here is Walters explaination:

<quote Walter quoting Stroustrup>

Stroustrup gives two examples (slightly modified here):

---------------------------------
class X1 { void f(int); }

// chain of derivations X(n) : X(n-1)

class X9: X8 { void f(double); }

void g(X9 p)
{
    p.f(1);    // X1.f or X9.f ?
}
-----------------------------------
His argument is that one can easilly miss an overload of f() somewhere in a
complex class heirarchy, and argues that one should not need to understand
everything about a class heirarchy in order to derive from it. The other
example involves operator=(), but since D doesn't allow overloading
operator=() instead I'll rewrite it as if it were a function that needs to
alter a class state, and a derived class written later that 'caches' a
computation on the derived state:

class B
{    long x;
     void set(long i) { x = i; }
    void set(int i) { x = i; }
    long squareIt() { return x * x; }
}
class D : B
{
    long square;
    void set(long i) { B.set(i); square = x * x; }
    long squareIt() { return square; }
}

Now, imagine B were a complex class with a lot of stuff in it, and our
optimizing programmer missed the existence of set(int). Then, one has:

    long foo(B b)
    {
        b.set(3);
        return b.squareIt();
    }

and we have an obscure bug.

</quote>

So, as you can see it's not for performance reasons but rather to avoid obscure bugs which when they manifest do so silently.

Regan
August 02, 2007
Regan Heath wrote:
> Steve Schveighoffer wrote:
>  > I was assuming that C++ got it right.  More than
>> I can't believe that C++ is not right, I'm surprised I never had
>> problems with it...
> 
> It's truly odd that no-one seems to have a problem with it in C++.  It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class.  Perhaps everyone just does that without thinking about it.

C++ has "using Base::method;" to replace alias for this particular case.
August 02, 2007
Regan Heath wrote:
> Steve Schveighoffer wrote:
>> Regan Heath Wrote:
> 
>  > I was assuming that C++ got it right.  More than
>> I can't believe that C++ is not right, I'm surprised I never had
>> problems with it...
> 
> It's truly odd that no-one seems to have a problem with it in C++.  It's not as if C++ even has 'alias' so it cannot pull symbols in, you're left re-implementing in the derived class.  Perhaps everyone just does that without thinking about it.

'using' serves this purpose in C++.

>> However, I think it should be changed.  Not sure if it will, as it
>> seems there  is already a precedent.  In my opinion, the base class
>> should be examined if the derived class does not provide a suitable
>> match.  I can't see how this would cause too much of a performance
>> hit
> 
> Performance isn't the reason against the Java behaviour, here is Walters explaination:
> 
> <quote Walter quoting Stroustrup>
> 
> Stroustrup gives two examples (slightly modified here):
> 
> ---------------------------------
> class X1 { void f(int); }
> 
> // chain of derivations X(n) : X(n-1)
> 
> class X9: X8 { void f(double); }
> 
> void g(X9 p)
> {
>     p.f(1);    // X1.f or X9.f ?
> }
> -----------------------------------
> His argument is that one can easilly miss an overload of f() somewhere in a
> complex class heirarchy, and argues that one should not need to understand
> everything about a class heirarchy in order to derive from it.

One key difference between D and C++ in this regard is that class methods in D are virtual by default, while in C++ they are not.  One could argue that this changes the implications of the "is a" relationship in D.  However, I do think the current rule is less error-prone, once understood.


Sean
August 02, 2007
Reply to Chris Nicholson-Sauls,

> I've never really 100% understood why this is the way that it is, but
> there /does/ exist a simple workaround.  Add this line to Y:
> alias super.foo foo;
> Yep, alias the superclass's method into the current class's scope.
> The problem has something to do with the lookup rules.  At the very
> least, I would think that declaring Y.foo with the 'override'
> attribute might give the wanted behavior, since surely a int(int)
> couldn't override a int(), yes?  But no, last I checked, it doesn't
> work either.
> 

And this suffers from the issue that a function name and a function are not the same thing, a function name (which can be alised) refers to ALL function overloads (which can't be aliased). This is the cause of many bugs and hack-arounds.

<coff> um... Walter?


August 03, 2007
On Thu, 02 Aug 2007 12:26:14 +0100, Regan Heath wrote:

> I would have thought that given your personal preference for being explicit in many cases you would prefer the C++ approach?

LOL ... it is obvious that I don't do much OO programming isn't it.

I turns out that I misunderstood the issue all this time. I did some playing around this morning and I now have a better knowledge base to work from.

My first, and biggest mistake, was thinking that EVERY name in the parent class was hidden from the derived class unless aliased. Now I realize that its only the ones with the same names explicitly defined in the derived class are hidden.

My second mistake was thinking that aliasing only worked for one level of derivation. In other words if C is derived from B, and B is derived from A, I thought that C couldn't get access to A methods unless they were aliased in B. I now know this is thinking is wrong.

I guess over time I'll get to appreciate this :)

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
3/08/2007 9:53:20 AM