При использовании повторяющегося шаблона шаблона дочерний тип является неполным типом в области действия родительского.
template<class CHILD>
struct Parent
{
CHILD _child; // <- there, incomplete
};
Вы не можете создать экземпляр неполного типа, поскольку его определения еще нет.
Итак ... почему?
Тип становится полным, когда компилятор встречает закрывающую скобку:
struct Hello {
// Technically not complete yet
};
// Complete here, we encountered the closing bracket.
Кроме того, родительские классы должны сами быть полными типами:
struct Incomplete;
struct NiceTry : Incomplete {}; // ERROR! `Incomplete` is an incomplete type.
Итак, у нас есть два правила: родительский класс класса должен быть полным, а тип не завершен до закрывающей скобки. Внутри родительского элемента CRTP мы не выполняем оба условия: родительский объект оценивается перед областью действия класса (они также располагаются перед областью действия класса в коде), и поскольку родительский элемент класса должен быть завершен, он должен быть завершен до дочерний класс. У вас не может быть взаимно завершенных типов в области действия класса, как бы вы ни старались:
struct class2;
struct class1 {
// Class 2 is incomplete here
};
struct class2 {
// class1 complete
};
Вы не можете иметь оба завершенных одновременно в обеих областях.
То же самое происходит с CRTP, здесь нет исключений.
Кроме того, ваш код примерно эквивалентен этому:
struct class1 {
class2 instance;
};
struct class2 {
class1 instance;
};
Если вы попытаетесь вычислить размер типов, вы столкнетесь с бесконечной рекурсией. У вас не может быть типа, который содержит сам себя.
Чтобы решить вашу проблему, не пытайтесь сдерживать ребенка. Вместо этого, поскольку вы на 100% знаете, какой класс является дочерним, просто приведите это:
template<typename Child>
struct parent {
void common_behavior() {
child().function1();
std::cout << child().member1 + child().member2;
}
private:
auto child() noexcept -> Child& { return *static_cast<Child*>(this); }
auto child() const noexcept -> Child const& { return *static_cast<Child const*>(this); }
};
И реализуйте дочерний элемент следующим образом:
struct child1 : parent<child1> {
void function1() { std::cout << "This is child 1"; }
int member1, member2;
};
struct child2 : parent<child2> {
void function1() { std::cout << "This is child 2"; }
float member1, member2;
};
Живой пример