недопустимое использование неполного типа - PullRequest
52 голосов
/ 17 марта 2009

Я пытаюсь использовать 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;
}

Это вывод, который я получаю:

sean@SEAN-PC:~/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’

Ответы [ 5 ]

61 голосов
/ 17 марта 2009

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

Так что получается, что 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 завершится неудачей.

24 голосов
/ 17 марта 2009

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

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;
} 
2 голосов
/ 17 марта 2009

Вы получаете 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> { ... };
1 голос
/ 17 марта 2009

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

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;
}
0 голосов
/ 17 марта 2009

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

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

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