On Tue, Jun 21, 2016 at 02:09:50AM +0000, Joerg Joergonson via Digitalmars-d-learn wrote: [...] > Lets suppose A -> B means B is derived from A. That is, any object of > B can be cast to A because the memory layout of A is contained in B > and any object of B can be accessed as if it were an A.
Correct. > Template parameters also can have this property since they are types. [...] The template *parameters* can also have this property. But the *template* itself may not. Contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { int[C.sizeof] data; } Templ!A a; Templ!B b; a = b; // should this be allowed? It should be clear that allowing this would cause problems, because in spite of the relationship between A and B, and hence the relationship between the template arguments of a and b, the same relationship does not hold between Templ!A and Templ!B (note that .data is an array of ints, not ubytes, and may not contain data in any layout that has any corresponds with the relationship between A and B). Another contrived example: class A { int x; } class B : A { int y; } struct Templ(C : class) { static if (C.sizeof > 4) { string x; } else { float y; } } Allowing implicit casting from Templ!B to Templ!A would not make sense, because even though the respective template arguments have an inheritance relationship, the Templ instantiation made from these classes has a completely unrelated and incompatible implementation. Now granted, these are contrived examples, and in real-life we may not have any real application that requires such strange code. However, the language itself allows such constructs, and therefore the compiler cannot blindly assume any relationship between Templ!A and Templ!B even though there is a clear relationship between A and B themselves. What should be done if we wish to allow converting Templ!B to Templ!A, though? One way (though this still does not allow implicit casting) is to use opCast: struct Templ(C : class) { ... // implementation here auto opCast(D : class) if (is(C : D)) // C must be a base class of D { ... // do something here to make the conversion // valid. Maybe something as simple as: return cast(Templ!D) this; // (provided that there are no memory layout // problems in Templ's implementation, of // course). } } Implementing this using opCast actually gives us an even more powerful tool: provided it is actually possible to convert between potentially incompatible binary layouts of Templ!A and Templ!B, the opCast method can be written in such a way as to construct Templ!A from Templ!B in a consistent way, e.g., by treating B as a subclass of A and calling the ctor of Templ!A, or, in the case of my contrived examples, do something non-trivial with the .data member so that the returned Templ!A makes sense for whatever purpose it's designed for. It allows the implementor of the template to specify exactly how to convert between the two types when the compiler can't possibly divine this on its own. This is kind of bringing a nuclear warhead to an anthill, though. In my own code where I have a template wrapping around types that need to convert to a common base type, I find it more useful to use the following pattern instead: class A { ... } class B : A { ... } class WrapperBase { ... // common stuff for all instantiations of Wrapper!X } class Wrapper(C : class) : WrapperBase { ... // stuff specific to C } Wrapper!A a; Wrapper!B b; WrapperBase wb = a; // OK wb = b; // also OK This may or may not be what you're looking for, though. T -- It said to install Windows 2000 or better, so I installed Linux instead.