Post by via Digitalmars-dPost by Walter Bright via Digitalmars-dPost by Timon Gehr via Digitalmars-dclass A
{
int i;
alias i this;
}
class B
{
int i;
alias i this;
}
class C
{
A a;
B b;
alias a this;
alias b this;
}
void foo(T)(T arg) if(is(T : int))
{
...
}
foo(C()); //Should it pass or not?
There's a rule with imports that if the same symbol is
reachable via multiple paths through the imports, that it is
not an ambiguity error. Here, the same type is reachable
through multiple alias this paths, so by analogy it shouldn't
be an error.
It's the same type, but different symbols; actual accesses
would be ambiguous. `is(T : int)` shouldn't evaluate to true if
`int a = T.init;` would fail.
I found an example of a situation that is bothering me.
Let we have a persistence framework, which provides a storing D
object in some persistence storage: DB, file et c.
In introduces paired functions store/load and special type
PersistenceObject.
If stored type is subtype of PersistenceObject it converts to
PersistenceObject and PersistenceObject.load(stream) called for
loading object (and PersistenceObject.store(stream) for storing).
Otherwice if object can't be converted to PersistenceObject it
should be serialized via "serialize" function (or deserialized
via "deserialize").
struct PersistenceFramework
{
void store(T)(T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.store(stream);
}
void store(T)(T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
store(serialize(arg));
}
void load(T)(ref T arg) if (is(T : PersistenceObject))
{
PersistenceObject po = arg;
arg.load(stream);
}
void load(T)(ref T arg) if (!is(T : PersistenceObject))
{
PersistenceObject po = arg;
load(serialize(arg));
}
Stream stream;
}
/****************************************************************
And we have the next types which we want to store and load
*****************************************************************/
struct Role
{
...
}
struct User
{
Role role;
PersistenceObject po;
//...
alias role this;
alias po this;
}
/*****************************************************************/
User u;
persistenceFramework.load(u)
//...
persistenceFramework.store(u);
/******************************************************************/
Role is not subtype of PersistenceObject thus all works ok.
We can store User via User.po and load it again;
Some time later, Role designer decided that Role should be
subtype of PersistenceObject and changed Role definition:
struct Role
{
...
PersistenceObject po;
alias po this;
}
Now, User can not be converted to PersistenceObject because there
are two path to convert: User.po and User.role.po;
Storing code after this change will be copiled successfully (if
we follow your "is" rule), however object will be tried to load
via "void load(T)(ref T arg) if (!is(T : PersistenceObject))".
Because object was saved via "void store(T)(T arg) if (is(T :
PersistenceObject))" at the previous program run, user will not
be loaded succesfully. Moreover, you will get an strange
unexpected program behaviour and will be hard to find real error
cause.
/*****************************************************************/
And finally, if you want to check, if you Type _really_ can be
converted to AnotherType, you can use the next check:
void foo(Type)(Type arg) if (is(typeof({AnotherType x =
Type.init;})))
{
}