Thread overview
Defining a single opCast disables explicit cast to base interfaces
Mar 17, 2015
Ali Çehreli
Mar 17, 2015
Ali Çehreli
Mar 17, 2015
jkpl
Mar 18, 2015
Jonathan M Davis
March 17, 2015
The following program compiles fine:

interface I
{}

class B : I
{}

class C : B
{
    int i;
}

void main()
{
    auto c = new C;

    auto i = cast(I)c;    // compiles
    auto b = cast(B)c;    // compiles
}

Let's add an unrelated opCast to C:

class C : B
{
    int i;

    int opCast(T : int)()
    {
        return i;
    }
}

Now the last two lines of main fail to compile:

Error: template instance opCast!(I) does not match template declaration opCast(T : int)()
Error: template instance opCast!(B) does not match template declaration opCast(T : int)()

Is this per spec? (Actually, where is the spec? (Trick question. ;) )

There is a workaround: Add a catch-all opCast that forwards to the all-powerful std.conv.to:

    T opCast(T)()
    {
        import std.conv;
        return this.to!T;
    }

Now it compiles and works as expected.

However, the question remains...

Thank you,
Ali
March 17, 2015
I forgot to mention that this discussion is carried over from the D.learn newsgroup:

  http://forum.dlang.org/thread/uwuvqurfqbetypdlwkdy@forum.dlang.org

Ali

March 17, 2015
On Tuesday, 17 March 2015 at 05:27:38 UTC, Ali Çehreli wrote:
> The following program compiles fine:
>
> interface I
> {}
>
> class B : I
> {}
>
> class C : B
> {
>     int i;
> }
>
> void main()
> {
>     auto c = new C;
>
>     auto i = cast(I)c;    // compiles
>     auto b = cast(B)c;    // compiles
> }
>
> Let's add an unrelated opCast to C:
>
> class C : B
> {
>     int i;
>
>     int opCast(T : int)()
>     {
>         return i;
>     }
> }
>
> Now the last two lines of main fail to compile:
>
> Error: template instance opCast!(I) does not match template declaration opCast(T : int)()
> Error: template instance opCast!(B) does not match template declaration opCast(T : int)()
>
> Is this per spec? (Actually, where is the spec? (Trick question. ;) )
>
> There is a workaround: Add a catch-all opCast that forwards to the all-powerful std.conv.to:
>
>     T opCast(T)()
>     {
>         import std.conv;
>         return this.to!T;
>     }
>
> Now it compiles and works as expected.
>
> However, the question remains...
>
> Thank you,
> Ali

There is also another trick: generally when something cannot be cast then it's often possible to cast it as a pointer to the cast type that is directly derefered. And it work...
---
    T opCast(T)()
    {
        static if(is(T==int))
            return i;
        else
            return *cast(T*) &this;
    }
---

...ed until version 2.067 (now it's deprecated because of &this since this is already a ptr).
March 18, 2015
On Monday, March 16, 2015 22:27:36 Ali Çehreli via Digitalmars-d wrote:
> The following program compiles fine:
>
> interface I
> {}
>
> class B : I
> {}
>
> class C : B
> {
>      int i;
> }
>
> void main()
> {
>      auto c = new C;
>
>      auto i = cast(I)c;    // compiles
>      auto b = cast(B)c;    // compiles
> }
>
> Let's add an unrelated opCast to C:
>
> class C : B
> {
>      int i;
>
>      int opCast(T : int)()
>      {
>          return i;
>      }
> }
>
> Now the last two lines of main fail to compile:
>
> Error: template instance opCast!(I) does not match template declaration
> opCast(T : int)()
> Error: template instance opCast!(B) does not match template declaration
> opCast(T : int)()
>
> Is this per spec? (Actually, where is the spec? (Trick question. ;) )
>
> There is a workaround: Add a catch-all opCast that forwards to the all-powerful std.conv.to:
>
>      T opCast(T)()
>      {
>          import std.conv;
>          return this.to!T;
>      }
>
> Now it compiles and works as expected.
>
> However, the question remains...

Defining opCast destroys basically all built-in casts, which I think is a horrible idea. The problem that you're describing was reported a couple of years ago:

https://issues.dlang.org/show_bug.cgi?id=9249

But it also affects stuff like shared, and a bug report for that was created four years ago:

https://issues.dlang.org/show_bug.cgi?id=5747

And there may be other bug reports for similar issues, but basically, as it stands, declaring opCast borks the built-in casts, forcing you to redefine them all, which is horrible IMHO. But Kenji expressed concern about fixing it in #5747, and no one has stepped up to sort it out.

- Jonathan M Davis