Рассмотрим следующий пример:
template <typename T>
class A {
private:
typedef typename T::C C;
};
template <typename T>
class B : public A<B<T>> {
public:
typedef T C;
};
int main() {
B<int> b;
}
Компиляция с GCC дает следующую ошибку:
test.cc:5:23: error: no type named 'C' in 'B<int>'
typedef typename T::C C;
~~~~~~~~~~~~^
test.cc:9:18: note: in instantiation of template class 'A<B<int> >' requested here
class B : public A<B<T>> {
^
test.cc:15:10: note: in instantiation of template class 'B<int>' requested here
B<int> b;
^
Почему компилятор выдает ошибку, если B::C
определяется и как это исправить?
С этой точки зрения,
class B : public A<B<T>> {
… учебный класс B
является неполным. Учебный класс A
не могу заглянуть внутрь.
C
определение типа внутри B
доступен с этой точки внутри B
и дальше. Он также доступен внутри функциональных органов в B
потому что вы можете рассматривать определение функции внутри определения класса как сокращение для размещения его после класса. Но неполный класс не содержит ничего, что видно со стороны: все, что может сделать внешний код, это сформировать указатели и ссылки и использовать класс в качестве аргумента шаблона.
template< class C >
using Ungood = typename C::Number;
struct S
{
void foo() { Number x; (void) x; } // OK
Ungood<S> uhuh; //! Nyet.
using Number = double;
};
auto main() -> int {}
Вы можете исправить свой код, изменив дизайн. Наиболее очевидным является передача типа в качестве отдельного аргумента шаблона. Но в зависимости от того, чего вы пытаетесь достичь, может оказаться, что наследство, которое у вас есть в данный момент, на самом деле не нужно или даже не полезно.
Вы не можете, потому что вы находитесь в парадоксе куриного яйца. Определение базы требует знания определения производного, для завершения которого необходимо определение базы. Вы просто должны придумать альтернативу. Одним из примеров может быть использование внешней метафункции для передачи необходимого типа тому, кто в нем нуждается. Надеюсь, это не входит в определение части членов базы, или вы, вероятно, облажались.
Другой альтернативой является передача T в качестве второго параметра.
Вы не можете сделать это из-за этот:
Класс считается определенным после того, как закрывающая скобка его спецификатора класса была замечена […]
И несколько исключений, ни одно из которых не действует в вашем случае.
Другими словами, вы должны рассматривать свой производный класс как не полностью определенный, когда пытаетесь использовать его в базовом классе для доступа к типу. C
,
В любом случае, вы можете использовать тот факт, что ваш производный класс является классом шаблона, и сделать это:
template <typename T>
class A;
template <template<typename> class D, typename T>
class A<D<T>> {
private:
using C = T;
};
Как видите, я не дал определения первичному шаблонному классу, поэтому можно использовать только специализацию для шаблонных классов.
Не уверен, что это реальный случай ФП, но это имеет место в примере в вопросе.