class VisitorBase;
template<typename R = void>
class BaseVisitable
{
public:
typedef R RetType;
virtual ~BaseVisitable() {}
// Ok,only need forward-declaration of VisitorBase
virtual R Accept(VisitorBase&) = 0;
protected:
template<typename T>
static RetType AcceptImp(T& visited, VisitorBase& guest) // same as above
{
// there is neither forward-declaration nor definition of Visitor type,
// how can it pass compilation?
if (Visitor<T>* p = dynamic_cast<Visitor<T>*>(&guest))
{
return p->Visit(visited);
}
// Visitor<int> v; error
}
};
这段代码摘自LOKI。我在上面的评论中写了一些疑惑。我无法理解为什么Vistor指针不需要前向声明但需要对象。谢谢。
答案 0 :(得分:0)
Visitor<T>
的前瞻性声明是不够的,因为Visitor<T>
调用p->Visit(visited)
的方法,这意味着必须声明Visitor<T>
通过包含语句或同一文件中Visitor<T>
的定义。否则,编译器不知道Visitor<T>
有成员Visit()
。
答案 1 :(得分:0)
我假设您的摘录来自文件Visitor.h
。如果您在该文件中包含 的前向声明template<class T> class Visitor;
,则摘录会编译。
Visitor
模板的前向声明足以调用Visitor<T>
上的函数,因为它是一个依赖类型,AcceptImp
模板的实例化将实例化Visitor<T>
模板最终给定的类型。到那时,必须定义Visitor<T>
(并且定义也必须具有被调用的函数)。如果没有,则会出现编译时错误。如果AcceptImp
模板根本没有实例化,则不会出现错误。
Visitor<int>
不是依赖类型,因此必须在AcceptImp
定义时定义,而不是在其实例化时 >。由于未定义Visitor
,因此无法实例化Visitor<int>
。
这是一个简化的演示:
// forward declaration
template<class S>
struct Foo;
template<class T>
void test(Foo<T>* f = nullptr) { // declaring a pointer is just fine
if(f != nullptr)
f->bar(); // calling a function is fine unless Foo<T> is incomplete or doesn't define bar
// Foo<int> i; // oops, incomplete type, cannot do
Foo<T> t; // Foo<T> must be complete when test<T> is instantiated, otherwise fails
}
// here's an example of what you can do
// let's define a specialization
template<>
struct Foo<int> {
void bar(){}; // if we didn't define this, then we couldn't instantiate test<int>
};
// now you can instantiate Foo<int> and call test
int main() {
Foo<int> i; // Foo<int> is a complete type now :)
test(&i);
// test<float>(); // oops, Foo<float> is incomplete
// test can't call Foo<float>::bar
// or instantiate an object of type Foo<float>
// Defining a pointer to Foo<T> in test would still be fine
}