Ошибка компиляции "Любопытно повторяющийся шаблон" C ++ "использует неопределенную структуру" - PullRequest
0 голосов
/ 10 июля 2020

У меня есть два класса, использующих шаблон шаблона "Любопытно повторяющееся" с родительским классом. Я включаю файлы неправильно или мне нужно сделать какое-то предварительное объявление?

Child1.h

#include "Parent.h"

struct Child1 : public Parent<Child1>
{
    void init()
    {
        sharedCode();
    }
}; 

Child2.h

#include "Parent.h"

struct Child2 : public Parent<Child2>
{
    void init()
    {
        sharedCode();
    }
}; 

Parent .h

template<class CHILD>
struct Parent
{
    void sharedCode()
    {
    }

    CHILD _child;
}; 

основной. cpp

#include "Child1.h"
#include "Child2.h"

int main()
{
    Child1 c1;
    Child2 c2;
}

1 Ответ

1 голос
/ 10 июля 2020

При использовании повторяющегося шаблона шаблона дочерний тип является неполным типом в области действия родительского.

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;
};

Живой пример

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...