虚拟模板函数:使用参数实现访客模式



我正在尝试实现用于行走 AST 的访问者模式。我已经定义了一个可以接受VisitorASTNode,并允许访问者自己访问。下面的示例分别包含访客和 ASTnode 的一个具体实现。

class ASTNode;
template <class P, class R>
class Visitor {
public:
virtual ~Visitor() {}
virtual R visit(ASTNode& node, P p) const = 0;
};
class ASTNode {
public:
virtual ~ASTNode() {}
template <class P, class R>
virtual R accept(Visitor<R, P>& v, P p) {
return v.visit(*this);
}
};
class Roman : public ASTNode {
public:
Roman(Numeral n, optional<Accidental> a) : numeral(n), alteration(a) {};
const Numeral numeral;
const optional<Accidental> alteration;
};
class ToStringVisitor : public Visitor<string, int> {
virtual string visit(Roman& node, int param) {
string result = NumeralStrings[node.numeral];
if (node.alteration.has_value()) result = accidentalToString(node.alteration.value()) + result;
return result;
}
};

然后,我可以使用这样的东西来遍历 AST:

Roman r;
ToStringVisitor tsv;
// ...
return r.accept(tsv, 42);

如您所见,我正在尝试使用模板来允许参数和返回值。但是,我收到编译器错误:

error: templates may not be 'virtual'
virtual R accept(Visitor<R, P>& v, P p) {

我对为什么这是一个错误有一个模糊的理解。但是,我如何合法地完成此操作?

编辑:我不认为这是这个问题的重复,因为我也在尝试让接受返回模板类型。

访问/接受函数不应接受额外的参数或返回除void以外的任何内容。具体的访客对象在施工时获取额外的数据。它还可以存储要从方法返回的任何结果。这是不需要任何模板的沼泽标准co结构。

class NodeVisitor {
virtual void visit (Roman*) = 0;
...
};
class ToStringVisitor : public NodeVisitor {
ToStringVisitor (int param) : param(param) {}
void visit (Roman* r) {
result = ...;
}
...
int param;
std::string result;
};

您会收到此错误消息,因为C++禁止定义虚拟模板函数。 删除 virtual 关键字将修复编译错误。

我刚刚写完一个解析器/词法分析器,发现使用 lambda 可以节省大量时间。

这是我对 lambda 访问者的实现。 它在VS 2017中编译,也应该在gcc下编译。

我从这个演讲中得到了代码:"C++Now 2017:维托里奥·罗密欧"使用 lambda 实现variant访问"

文件匹配.h

#pragma once
#include <type_traits>
#include <variant>
template<typename TF, typename...TFs>
struct overload_set : TF, overload_set<TFs...>
{
using TF::operator();
using overload_set<TFs...>::operator();
template<typename TFFwd, typename...TFFwds>
constexpr overload_set(TFFwd&& f, TFFwds&&...rest)
: TF { std::forward<TFFwd>(f) }
, overload_set<TFs...>{ std::forward<TFFwds>(rest)... }
{ }
};
template<typename TF>
struct overload_set<TF> : TF
{
using TF::operator();
template<typename TFFwd>
constexpr overload_set(TFFwd&& f)
: TF { std::forward<TFFwd>(f) }
{ }
};
template<typename...Tfs>
constexpr auto overload(Tfs&...fs)
{
return overload_set<std::remove_reference_t<Tfs>...>(std::forward<Tfs>(fs)...);
}
template<typename Visitor, typename...TVariants>
constexpr decltype(auto) visit_recursively(Visitor&& vis, TVariants&&...vars)
{
return std::visit(
std::forward<Visitor>(vis),
std::forward<TVariants>(variants)._data...
);
}
template<typename...TVariants>
constexpr auto match(TVariants&&...vs)
{
return [&vs...](auto&&...fs) //-> decltype(auto)
{
return std::visit(overload(std::forward<decltype(fs)>(fs)...), vs...);
};
}

解释器级别的示例:

template<>
std::string convertTo<std::string>(const variant& v)
{
return match(v)(
[](const std::string& s) { return s; },
[](const auto&) { throw InternalError("cannot convert type to string"); return std::string{}; }
);
}
variant Interpreter::evaluate(ast::RValue & rv)
{
// maps to overloads for all the types held by variant type RValue
return match(rv)(
[&](auto& x) { return evaluate(x); }
);
}
// recursion...
variant evaluate(std::unique_ptr<ast::Spheref>& rv)
{
return match(rv->vec_) (
[](ast::Vector4f& v) { return variant{ std::make_shared<Vector4f>(std::move(v)) }; },
[&](std::vector<ast::RValue>& v)
{
if (v.size() != 4)
{
throw InternalError{ "sphere must have 4 parameters" };
}
Vector4f r;
r[0] = convertTo<F32>(evaluate(v[0]));
r[1] = convertTo<F32>(evaluate(v[1]));
r[2] = convertTo<F32>(evaluate(v[2]));
r[3] = convertTo<F32>(evaluate(v[3]));
return variant{ std::make_shared<Vector4f>(std::move(r)) };
}
);
}
// cascading calls...
ObjectRef or = match(o->value_) (
[](Identifier& id) -> ObjectRef
{ 
return { std::make_shared<ast::Identifier>(std::move(id)) };  
},
[&](ast::ObjectValueBlock& bl) -> ObjectRef
{
return match(std::move(evaluate(bl))) (
[](std::shared_ptr<Object>&& x) { return ObjectRef{ x }; },
[](std::shared_ptr<Identifier>&& x) { return ObjectRef{ x };},
[](auto&&) { 
throw InternalError{ "unexpected type in Object array expansion" }; 
return ObjectRef{}; 
}
);
);

最新更新