Why can't we derive struct's?
Steven Schveighoffer
schveiguy at gmail.com
Fri Dec 21 22:23:20 UTC 2018
On 12/21/18 4:46 PM, Walter Bright wrote:
> On 12/21/2018 11:08 AM, Steven Schveighoffer wrote:
>>> The issues I've seen in bugzilla are how alias this overrides default
>>> expected
>> behavior of classes. In other words, if you have a base class and want
>> to cast it to a derived, and the base class has an alias this, it
>> stupidly tries the alias this, and won't even consider a dynamic cast.
>>
>> It's not madness so much as poor implementation. Saying there are bugs
>> in the alias this implementation is not the same as saying the feature
>> is problematic. All that is needed is a good definition of what takes
>> precedence -- we already have that, it's just not implemented properly.
>
> The ones I reviewed do not have easy answers as to what the correct
> behavior "ought" to be, and it certainly is not spec'ed in the
> specification.
Which ones?
Fundamentally, alias this brings behavior for types where default
behavior doesn't exist. For classes, casting to void * or another object
is defined, so alias this should not override. Principal of least
surprise. That's the only bug I've seen.
>> But MI is not multiple alias-this.
>
> It fundamentally is. (Though I agree it doesn't have the virtual base
> "diamond inheritance" issue.)
In terms of which alias this is used, we have leeway to define whatever
we want. Alias this acts like inheritance, but it's NOT inheritance. We
can say there is a specific order of precedence between the alias this'd
items (I wouldn't recommend that), or we could say more than one alias
this being usable in the same expression is an ambiguity error.
I don't think the precedence rules are that complicated:
1. The type's members or base classes
2. The type's alias this'd members. Any ambiguities are a compiler error.
3. UFCS functions that accept the type or base classes.
4. UFCS functions that accept any alias this'd type. Any ambiguities are
a compiler error.
Unlike MI, there is a specific member you can use to disambiguate, which
is way less clunky than MI, where you need a cast, or to name the type
itself.
My vision:
void foo(int x) { writeln("foo int"); }
void foo(T t) { writeln("foo T"); }
void bar(int x) { writeln("bar int"); }
void baz(int x) { writeln("baz int"); }
void baz(T t) { writeln("baz T"); }
void baz(S s) { writeln("baz S"); }
void foobar(int x) { writeln("foobar int"); }
void foobar(T t) { writeln("foobar T"); }
struct T
{
void bar() { writeln("bar T"); }
}
struct S
{
int x;
T t;
alias this x;
alias this t;
void foo() { writeln("S");}
}
S s;
s.foo(); // foo S
s.bar(); // bar T; members take precedence over UFCS
s.baz(); // baz S; main type takes precedence over alias this'd
s.foobar(); // Error, both alias this'd have same precednece for UFCS
s.x.foobar(); // foobar int
s.t.foobar(); // foobar T
Don't have the "both alias this'd have the same member" error, but you
get the idea.
And your example from earlier:
class C : B
{
B b;
alias b this;
}
Basically, the alias this b is never used, because C will always take
precedence.
If you think any of this is unintuitive, please let me know.
-Steve
More information about the Digitalmars-d
mailing list