Проблема наблюдается в компиляторах как C++, так и C#, и, возможно, других языков со статической типизацией и параметризированными классами.
Есть классы:
template<class A, class B> class Z {};
template<class C, class D> class Y {};
template<class E, class F> class X: public Z<Y<E, F>, F> {};
template<class G, class H> class W: public Y<G, H> {};
class V {};
class U {};
class T: public X<W<V, U>, U> {};
Z<W<V, U>, U> *a = new T();
Чисто логически и теоретически последняя строчка должна выполняться, поскольку T - потомок Z, несмотря на параметры. На уровне байтов памяти (в случае reinterpret_cast) совместимость будет. Однако компиляторы заявляют, что приведение типов невозможно.
Компилятор работает последовательно: сначала он подставляет параметры шаблонов, и только потом полученный код обрабатывается на предмет сочетаемости типов в силу наследования.
Если бы компилятор перемежал подстановку шаблонов и проверки типов, проблемы бы не было. Но была бы другая проблема - долгая работа компилятора, поскольку логический вывод типа в таком случае - это переборная задача.
В случае перебора по аналогии с тем, как работает среда исполнения Prolog, дерево поиска выглядело бы следующим образом:
T
:
[
E = W<V, U>
F = U
]
X<W<V,U>,U>
= [
W<V,U>
: [
C = G = V
D = H = U
]
Y<V,U>
]
X<Y<V,U>,U>
: [
A = E = Y<U, V>
B = F = U
]
Z<Y<Y<V,U>,U>,U> !
: [
A = Y<E,F>
= [
E = V
F = U
]
Y<V,U>
B = F = U
]
Z<Y<V,U>,U> !
: [
A = E = W<V,U>
B = F = U
]
Z<Y<W<V,U>,U>,U> !
= [
W<V,U>
: [
C = G = V
D = H = U
]
Y<V,U>
]
Z<Y<Y<V,U>,U>,U> !
: [
A = Y<E,F>
= [
E = V
F = U
]
W<V,U>
: [
C = G = V
D = H = U
]
Y<V,U>
B = F = U
]
Z<Y<V,U>,U> !
: - наследование, = - подстановка, [] - предварительные вычисления, ! - результат
Из этого следует: во-первых и главное, неопределённость результатов типизации, которая заставит компилятор хранить все варианты типов для согласования разных мест программы, каждый раз подбирая наиболее подходящее соответствие и умножая неопределённость типа в расчётных цепочках (особенно в свете новомодных деклараций с автоматической типизацией - auto и var); во-вторых, пример благополучно разрешился бы.
Но... реальность грубее и примитивнее. У компиляторов лишь один вариант приведения: Z<Y<Y<V,U>,U>,U>. С одной стороны, это хорошо, что один, с другой стороны, это следствие упрощённых алгоритмов анализа кода с минимумом отложенных решений.