使用不完整类型无效 - 为什么在这种情况下没有错误?

时间:2014-09-25 13:56:15

标签: c++ templates incomplete-type

我很困惑为什么我的代码没有产生错误invalid use of incomplete type,而我对此错误所做的所有阅读都表明应该这样做。
问题源于此错误在我的代码中具有类似结构的部分出现(如预期),但我无法在 small 示例中重现它(请参阅<问题末尾的em>免责声明。

我想要做的总结:

  • 我有一个结构(Tree),我想为它分配基本类型First的不同对象。
  • First的具体实现具有不同的返回值,因此使用了两个间接级别:
  • First是一个抽象基类,First *用于处理不同的具体实例。
  • template <typename Type> class TypedFirst : public First是一个抽象类型,它定义了返回类型为Type的函数。
  • 最后ConcreteFirstXTypedFirst<Type>
  • 的具体专精

tree.tpp中,为什么调用new TF(this) 而不是会产生invalid use of incomplete type错误? (该位置标记在代码中)我认为错误应该在那里,因为虽然TF是一个模板,但当我使用ConcreteFirstA时,tree.tpp并不知道它(它不包括concretefirsta.h甚至first.h转发声明First

可以找到此示例的完整,可编译和可运行的代码here on pastebin。在这里,为简洁起见,我将排除#define警卫和类似的事情。代码如下:

// tree.h
class First;
class Tree{
  public:
    Tree() {}
    ~Tree() {}
    template<class TF> // where TF is a ConcreteFirst
    void addFirstToTree();
  private:
    std::map<std::string, First *> firstCollection; // <- "First"'s here
};
#include "tree.tpp"

// tree.tpp
#include "tree.h"

template <class TF> // where TF is a ConcreteFirst
void Tree::addFirstToTree(){
  this->firstCollection[TF::name] = new TF(this); // <--- Why does this work?
  //                                ^^^^^^^^^^^^^      
}

// first.h
class Tree;
class First{
  public:
    static const std::string name;
    First(const Tree *baseTree) : myTree(baseTree) {}
    virtual ~First();
  protected:
    const Tree *myTree;
};

template <typename Type> class TypedFirst : public First{
  public:
    static const std::string name;
    TypedFirst(const Tree *baseTree) : First(baseTree) {}
    Type &value() {return this->_value;}
  private:
    Type _value;
};
#include "first.tpp"

// first.tpp
#include "first.h"
template <typename Type>
const std::string TypedFirst<Type>::name = "default typed";

// first.cpp
#include "first.h"
First::~First() {}
const std::string First::name = "default";

// concretefirsta.h
#include "first.h"
class ConcreteFirstA : public TypedFirst<int>{
  public:
    static const std::string name;
    ConcreteFirstA(const Tree *baseTree) : TypedFirst<int>(baseTree) {}
    ~ConcreteFirstA() {}
};

// concretefirsta.cpp
#include "concretefirsta.h"
const std::string ConcreteFirstA::name = "firstA";

最后,代码将所有这些组合在一起并使(in)适当的函数调用:

// main.cpp
#include "tree.h"
#include "first.h"
#include "concretefirsta.h"

int main(){
  Tree *myTree = new Tree();
  myTree->addFirstToTree<ConcreteFirstA>(); // <-- here! why is this working?
  delete myTree;
  return 0;
}

免责声明这个问题实际上是由我遇到的一个更大的问题所驱动的,我觉得Stack Overflow格式太大而无法解决。即使我最初尝试过这个问题,但问题仍然过于宽泛,我现在试图通过只问一部分问题来挽救它。

我的问题是我在一段与此相同的结构代码中不断收到错误:但是,我无法在一个小例子中重现它。

因此,我问为什么以下代码产生错误 invalid use of incomplete type(正如我所料),我希望帮助我理解并解决我的实际问题。

请不要告诉我这是the XY problem的情况:我知道我不是在询问我的实际问题,因为我(和社区)认为这个格式太大了。

2 个答案:

答案 0 :(得分:2)

main.cpp包含tree.h,其中(间接)包含有问题的addFirstToTree()模板,concretefirsta.h包含ConcreteFirstA的定义。

main.cpp中稍后,addFirstToTree()类型实例化了ConcreteFirstA

myTree->addFirstToTree<ConcreteFirstA>(); // <-- here! why is this working?

这是编译器需要足够了解用作模板参数的类型才能编译addFirstToTree()的点。它确实足够了解。 ConcreteFirstA是一个完整的类型,因为concretefirsta.h已包含在内并包含类定义。


早些时候在tree.tpp中定义了addFirstToTree()ConcreteFirstA尚未定义,但这无关紧要。编译器看到一个模板,并且不知道稍后将实例化该模板的模板参数。任何依赖于模板参数的函数/ ...(“依赖名称”)都无法在不知道模板将被实例化的参数的情况下解析,因此名称查找/ ...将被推迟到以后。

实例化模板后,编译器会解析所有相关名称并编译特定模板参数的模板。由于此时ConcreteFirstA不是一个不完整的类型,因此无误。

答案 1 :(得分:1)

因为模板在用具体参数实例化之前不会被编译。

当编译器出现时:

myTree->addFirstToTree<ConcreteFirstA>();

它首次使用参数addFirstToTree编译函数ConcreteFirstA,这是一个完全已知的类型。

请参阅cplusplus.com - Templates

  

它们是按需编译的,这意味着在需要具有特定模板参数的实例化之前,不会编译模板函数的代码。此时,当需要实例化时,编译器会专门为模板中的参数生成一个函数。