Я хочу реализовать шаблон Visitor в C ++ следующим образом:
class Visitor{
public:
virtual ~Visitor();
virtual void visit(C & t)=0;
};
class V : public Visitor{
public:
void visit(C &c);
};
class C{
public:
void accept(Visitor &v){ v.visit(*this); }
};
Но компилятор жалуется на две синтаксические ошибки:
Неизвестный идентификатор C и посетитель.
В чем проблема?
На данный момент компилятор видит
virtual void visit(C & t)=0;
название C
неизвестно
Вам необходимо переслать-объявить class C
до Visitor
class C;
class Visitor{
...
}
На четвертой строке никто не знает, что C
является. Это неизвестный идентификатор.
Это делает определение Visitor
не действует, следовательно, когда вы пытаетесь использовать свой Visitor
позже возникает другая ошибка.
Проблема в том, что класс C не определен в тот момент, когда посетитель использует его. Либо переместите его наверх (класс С), либо:
class C;
Добавьте вышеуказанное предварительное объявление вверху файла. Поскольку вы используете его только в качестве ссылочного параметра, этого должно быть достаточно.
В языке C ++ компилятор не будет искать имена, которые еще предстоит определить, или, лучше сказать, иногда это происходит, а иногда нет.
Можно сказать например
struct Foo
{
Foo(int x) : m_x(x) { }
int m_x;
};
и компилятор не будет жаловаться, даже если вы использовали m_x
прежде чем определить, что m_x
есть, но на уровне модуля этот взгляд вперед не представлен:
struct Foo
{
Bar *p; // Error, Compiler doesn't know what Bar is
};
// Too late, the compiler is not going to read down here while
// analyzing Foo.
struct Bar
{
int x;
};
Как вы решаете случаи, когда вам нужно что-то использовать, прежде чем определять это? Используя специальную «предварительную декларацию», в которой вы только заявляете, что будет что-то с таким именем, и вы позже определяете, что именно в конкретном … например
struct Foo; // There will be a struct Foo defined somewhere
struct Bar
{
Foo *p; // Fine, even if the compiler doesn't really know Foo
};
struct Foo
{
Bar *q; // Fine and no forward needed... Bar is known at this point
};
Более или менее правило таково: в одном классе все методы могут видеть все другие методы и все члены, даже если они определены позже в классе, вместо этого каждое имя должно быть известно, прежде чем его можно будет использовать.
Иногда требуется более сложный шаблон, например,
struct Foo;
struct Bar
{
void doit(Bar x);
};
struct Foo
{
void doit_too(Foo x);
};
void Foo::doit(Bar x) { ... }
void Bar::doit_too(Foo x) { ... }
В последнем случае вы вынуждены поставить реализацию обоих методов после объявлений обоих классов, потому что просто зная, что Foo
этого класса недостаточно, чтобы иметь возможность компилировать операцию копирования (обратите внимание, что параметр в методах был передан по значению, а не по указателю или ссылке).