57

我正在尝试在我的项目中使用来自子类的 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’
4

5 回答 5

68

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

所以发生的事情是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不是正确的类型,传递vardo_action将失败。

于 2009-03-16T21:36:47.770 回答
28

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

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;
} 
于 2009-03-16T22:07:47.540 回答
2

你派生BA<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> { ... };
于 2009-03-16T21:29:29.053 回答
1

不完全是您所要求的,但您可以将操作设为模板成员函数:

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;
}
于 2009-03-16T21:45:05.240 回答
0

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

而是尝试:

void action(const typename Subclass::mytype &var) {
            (static_cast<Subclass*>(this))->do_action();
    }
于 2009-03-16T21:17:15.370 回答