我知道有两个很好的例子,其中访问者明显优于迭代器。
首先是与一些未知的类成员集进行交互,尤其是在 C++ 中。例如,这是一个打印出其他类的所有成员的访问者。假设您是Printer 的作者,而您不认识的人是Heterogeneous3Tuple 的作者。
#include <iostream>
template<class ElemType1, class ElemType2, class ElemType3>
class Heterogeneous3Tuple
{
public:
Heterogeneous3Tuple(ElemType1 elem1, ElemType2 elem2, ElemType3 elem3)
: elem1_(std::move(elem1)), elem2_(std::move(elem2)), elem3_(std::move(elem3))
{}
template<class Visitor>
void accept(const Visitor& visitor)
{
visitor(elem1_);
visitor(elem2_);
visitor(elem3_);
}
private:
ElemType1 elem1_;
ElemType2 elem2_;
ElemType3 elem3_;
};
class Printer
{
public:
template<class VisitedElemType>
void operator()(const VisitedElemType& visitee) const
{
std::cout << visitee << std::endl;
}
private:
};
int main() {
Heterogeneous3Tuple<char, int, double> h3t('a', 0, 3.14);
Printer p;
h3t.accept(p);
}
a
0
3.14
coliru
没有明智的方法让迭代器在这里工作。甚至不知道我们的 Printer 类可能会与此交互的类型,只要访问者是 accept()ed 并且元素都以类似的方式与 operator << 和流交互。
我知道的另一个很好的例子出现在抽象语法树操作中。 CPython 和 LLVM 都使用访问者。在这里使用访问者可以防止操作某些 AST 节点的代码需要知道如何迭代所有可能以复杂方式分支的各种 AST 节点。 LLVM source code 更详细。这是重点:
/// Instruction visitors are used when you want to perform different actions
/// for different kinds of instructions without having to use lots of casts
/// and a big switch statement (in your code, that is).
///
/// To define your own visitor, inherit from this class, specifying your
/// new type for the 'SubClass' template parameter, and "override" visitXXX
/// functions in your class. I say "override" because this class is defined
/// in terms of statically resolved overloading, not virtual functions.
///
/// For example, here is a visitor that counts the number of malloc
/// instructions processed:
///
/// /// Declare the class. Note that we derive from InstVisitor instantiated
/// /// with _our new subclasses_ type.
/// ///
/// struct CountAllocaVisitor : public InstVisitor<CountAllocaVisitor> {
/// unsigned Count;
/// CountAllocaVisitor() : Count(0) {}
///
/// void visitAllocaInst(AllocaInst &AI) { ++Count; }
/// };
///
/// And this class would be used like this:
/// CountAllocaVisitor CAV;
/// CAV.visit(function);
/// NumAllocas = CAV.Count;