C++ 不完整类型的无效使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/652155/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:30:48  来源:igfitidea点击:

invalid use of incomplete type

c++templatestypedefcrtp

提问by seanhodges

I'm trying to use a typedef from a subclass in my project, I've isolated my problem in the example below.

我试图在我的项目中使用来自子类的 typedef,我在下面的示例中隔离了我的问题。

Does anyone know where I'm going wrong?

有谁知道我哪里错了?

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

This is the output I get:

这是我得到的输出:

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'

回答by Johannes Schaub - litb

The reason is that when instantiating a class template, all its declarations (not the definitions) of its member functions are instantiated too. The class template is instantiated precisely when the full definition of a specialization is required. That is the case when it is used as a base class for example, as in your case.

原因是当实例化一个类模板时,它的成员函数的所有声明(而不是定义)也被实例化。当需要专门化的完整定义时,类模板被精确地实例化。例如,当它用作基类时就是这种情况,就像你的情况一样。

So what happens is that A<B>is instantiated at

所以发生的事情是A<B>

class B : public A<B>

at which point Bis not a complete type yet (it is after the closing brace of the class definition). However, A<B>::action's declaration requires Bto be complete, because it is crawling in the scope of it:

此时B还不是一个完整的类型(它在类定义的右大括号之后)。但是,A<B>::action的声明需要B完整,因为它在它的范围内爬行:

Subclass::mytype

What you need to do is delaying the instantiation to some point at which Bis complete. One way of doing this is to modify the declaration of actionto make it a member template.

您需要做的是将实例化延迟到B完成的某个点。一种方法是修改 的声明action以使其成为成员模板。

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

It is still type-safe because if varis not of the right type, passing varto do_actionwill fail.

它仍然是类型安全的,因为如果var不是正确的类型,传递vardo_action将失败。

回答by Martin York

You can get around this by using a traits class:
It requires you set up a specialsed traits class for each actuall class you use.

您可以通过使用特性类来解决这个问题:
它要求您为每个实际使用的类设置一个特殊的特性类。

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

回答by sth

You derive Bfrom A<B>, so the first thing the compiler does, once it sees the definition of class Bis to try to instantiate A<B>. To do this it needs to known B::mytypefor the parameter of action. But since the compiler is just in the process of figuring out the actual definition of B, it doesn't know this type yet and you get an error.

B从派生A<B>,因此编译器在看到类的定义后所做的第一件事B就是尝试实例化A<B>。为此,它需要知道B::mytype的参数action。但是由于编译器只是在弄清楚 的实际定义的过程中B,它还不知道这种类型,并且您会收到错误消息。

One way around this is would be to declare the parameter type as another template parameter, instead of inside the derived class:

解决此问题的一种方法是将参数类型声明为另一个模板参数,而不是在派生类中:

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

回答by John Dibling

Not exactly what you were asking, but you can make action a template member function:

不完全是您要问的,但是您可以将 action 设为模板成员函数:

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

回答by Andreas Magnusson

You need to use a pointer or a reference as the proper type is not known at this time the compiler can not instantiate it.

您需要使用指针或引用,因为此时不知道正确的类型,编译器无法实例化它。

Instead try:

而是尝试:

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