Подтвердить что ты не робот

Недопустимое использование неполного типа

Я пытаюсь использовать typedef из подкласса в моем проекте, я выделил свою проблему в примере ниже.

Кто-нибудь знает, где я ошибаюсь?

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        void action(typename Subclass::mytype var) {
            (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
            // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}

Это результат, который я получаю:

[email protected]:~/Documents/LucadeStudios/experiments$ g++ -o test test.cpp
test.cpp: In instantiation of ‘A<B>’:
test.cpp:10:   instantiated from here
test.cpp:5: error: invalid use of incomplete type ‘class B’
test.cpp:10: error: forward declaration of ‘class B’
4b9b3361

Ответ 1

Причина в том, что при создании экземпляра шаблона класса все его объявления (а не определения) его функций-членов также создаются. Шаблон класса создается именно тогда, когда требуется полное определение специализации. Это тот случай, когда он используется как базовый класс, например, как в вашем случае.

Итак, что происходит, когда A<B> создается в

class B : public A<B>

в котором точка B еще не является полным типом (она находится после закрывающей скобки определения класса). Однако для объявления A<B>::action требуется, чтобы B был полным, потому что он сканирует его область действия:

Subclass::mytype

Что вам нужно сделать, так это отложить создание экземпляра до некоторой точки, в которой завершено B. Один из способов сделать это - изменить объявление action, чтобы сделать его шаблоном-членом.

template<typename T>
void action(T var) {
    (static_cast<Subclass*>(this))->do_action(var);
}

Он по-прежнему безопасен по типу, потому что если var не имеет нужного типа, передача var в do_action завершится с ошибкой.

Ответ 2

Вы можете обойти это, используя класс признаков:
Это требует, чтобы вы создали специальный класс признаков для каждого используемого класса.

template<typename SubClass>
class SubClass_traits
{};

template<typename Subclass>
class A {
    public:
        void action(typename SubClass_traits<Subclass>::mytype var)
        {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};


// Definitions for B
class B;   // Forward declare

template<> // Define traits for B. So other classes can use it.
class SubClass_traits<B>
{
    public:
        typedef int mytype;
};

// Define B
class B : public A<B>
{
    // Define mytype in terms of the traits type.
    typedef SubClass_traits<B>::mytype  mytype;
    public:

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv)
{
    B myInstance;
    return 0;
} 

Ответ 3

Вы получаете B от A<B>, поэтому первое, что делает компилятор, когда он видит определение класса B, - это попытаться создать экземпляр A<B>. Для этого необходимо знать B::mytype для параметра action. Но поскольку компилятор сейчас находится в процессе определения фактического определения B, он еще не знает этого типа, и вы получите сообщение об ошибке.

Один из способов заключается в том, чтобы объявить тип параметра в качестве другого параметра шаблона, а не внутри производного класса:

template<typename Subclass, typename Param>
class A {
    public:
        void action(Param var) {
                (static_cast<Subclass*>(this))->do_action(var);
        }
};

class B : public A<B, int> { ... };

Ответ 4

Не совсем то, что вы просили, но вы можете сделать действие функцией члена шаблона:

template<typename Subclass>
class A {
    public:
        //Why doesn't it like this?
        template<class V> void action(V var) {
                (static_cast<Subclass*>(this))->do_action();
        }
};

class B : public A<B> {
    public:
        typedef int mytype;

        B() {}

        void do_action(mytype var) {
                // Do stuff
        }
};

int main(int argc, char** argv) {
    B myInstance;
    return 0;
}

Ответ 5

Вам нужно использовать указатель или ссылку, поскольку соответствующий тип не известен в это время, когда компилятор не может его создать.

Вместо этого попробуйте:

void action(const typename Subclass::mytype &var) {
            (static_cast<Subclass*>(this))->do_action();
    }