我正在尝试在Boost.Spirit中编写一个shell语言解析器。但是,我不清楚有关rule
s语义的一些基本问题。
查看文档,r.alias()
的成员r.copy()
和rule
。 IIUC,这些成员应分别返回对规则的引用和规则内容的副本。但是,我没有明确说明当我在另一个规则的定义中使用该规则时会发生什么。根据我的实验,我发现相互递归规则可以通过以下方式定义:
rule<Iter> r1, r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;
表明规则是通过解析器表达式中的引用来获取的。问题是,当变量超出范围时它会做什么,例如:
rule<Iter> r1;
{
rule<Iter> r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;
}
... // use r1
在同一个注释中,将从包含rvalue类型规则工作的解析表达式中分配规则(r.copy()
也将是rule
类型的右值,不是吗?例如
rule<Iter> f() { return char_('a') << char_('b'); }
rule<Iter> r1 = ... << f();
任何人都可以了解rule
的副本和引用的详细语义,并可能纠正这篇文章中的任何误解吗?
答案 0 :(得分:13)
答案取决于你所指的精神版本。
Spirit.Classic(前Spirit V1.x)为规则实现特殊的复制语义。文档说:
在任何地方引用规则时 EBNF的右侧 表达,规则是由 通过引用表达。它是 客户的责任是确保 引用的规则保留在 范围,不会被破坏 正在被引用。
赋值运算符实际上也引用了rhs规则,而不创建深层副本。这样做是为了允许:
rule<> r1, r2;
r1 = ...;
r2 = r1;
但事实证明这是一种高度混乱,因为它阻止处理规则的方式与“普通”对象相同。
由于这个原因,有成员函数rule::copy()
,允许制作规则的显式深层副本(例如将它们存储在STL容器中)。
与此同时:
r2 = r1.copy();
是完全错误的。 r2
将引用从r1
函数返回的copy()
的(被破坏的)临时副本。
在Spirit.Qi(即Spirit V2.x)中,行为部分改变。当在解析器之外处理时,规则现在按预期运行。您可以将它们正常存储在容器中(赋值运算符公开预期的行为)。但请注意,解析器中的表达式规则仍然通过引用保留,这仍然允许以与以前相同的方式引用规则:
rule<> r1, r2;
r1 = ... >> r2 >> ...;
r2 = ... >> r1 >> ...;
有时需要制作规则的深层副本,因此仍然存在成员功能copy
。
更改的复制语义有另一个副作用。构造如:
r1 = r2;
现在正在创建r2
的(深层)副本,这可能不是您所期望的,特别是如果r2
仅在被“分配”到r1
后才会分配其rhs 。出于这个原因,有一个新的成员函数alias
为这个角点案例启用了引用语义:
r1 = r2.alias();
在任何情况下,在两个版本的Spirit中,如果从解析器表达式引用的部分规则超出范围,您将最终获得悬空引用。
BTW,Spirit版本都没有实现函数rule::ref()
。