【问题标题】:invalid use of incomplete type不完整类型的无效使用
【发布时间】:2010-10-13 17:19:58
【问题描述】:

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

【问题讨论】:

    标签: c++ templates typedef crtp


    【解决方案1】:

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

    所以发生的事情是 A&lt;B&gt;

    处实例化
    class B : public A<B>
    

    此时B 还不是一个完整的类型(它在类定义的右大括号之后)。但是A&lt;B&gt;::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;
    } 
    

    【讨论】:

    • 为什么派生类typedef SubClass_traits&lt;B&gt;::mytype mytype;中的B?我正在实现这样的东西,我想知道在B中使用实际类型是否更清楚。
    • @RL-S。因此,如果您更改类型(一年后更新代码时),您只需在一个地方更改它。 B 中的 typedef 的原因是为了使函数调用 do_action(mytype var) 更容易阅读,因为替代方案是 do_action(typename SubClass_traits&lt;B&gt;::mytype var)
    【解决方案3】:

    您从A&lt;B&gt; 派生B,因此编译器在看到类B 的定义后首先要做的就是尝试实例化A&lt;B&gt;。为此,它需要知道B::mytypeaction 参数。但是由于编译器只是在找出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();
            }
        

        【讨论】:

        • 我尝试将其更改为引用,然后更改为指针,但错误仍然相同。不过我理解你的意思。
        猜你喜欢
        • 2017-10-24
        • 2013-05-19
        • 2016-04-06
        • 2017-07-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多