我很困惑为什么我的代码没有产生错误invalid use of incomplete type
,而我对此错误所做的所有阅读都表明应该这样做。
问题源于此错误在我的代码中具有类似结构的部分出现(如预期),但我无法在 small 示例中重现它(请参阅<问题末尾的em>免责声明。
我想要做的总结:
Tree
),我想为它分配基本类型First
的不同对象。First
的具体实现具有不同的返回值,因此使用了两个间接级别:First
是一个抽象基类,First *
用于处理不同的具体实例。template <typename Type> class TypedFirst : public First
是一个抽象类型,它定义了返回类型为Type
的函数。ConcreteFirstX
是TypedFirst<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的情况:我知道我不是在询问我的实际问题,因为我(和社区)认为这个格式太大了。
答案 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
,这是一个完全已知的类型。
它们是按需编译的,这意味着在需要具有特定模板参数的实例化之前,不会编译模板函数的代码。此时,当需要实例化时,编译器会专门为模板中的参数生成一个函数。