为什么我们在解析器文件中定义联合时使用复杂 类 的指针?

Why do we use pointers for complex classes when defining union in parser file?

我正在为 RDDL 开发解析器,正如我之前所做的那样,当我定义包含我使用的类型的联合时,我使用指针。例如

%union {
    double d;
    int i;
    std::string *str;
    std::vector<std::string> *vectorStr;

    RDDLBlock *rddlBlock;
    Domain *domain;
    DefineType *defineType;
    std::vector<DefineType*> *vectorDefineType;
    DomainList *domainList;
    std::vector<PvarDefinition*> *vectorPvarDefinition;
    PvarDefinition *pVarDefinition;
    CpfDefinition *cpfDefinition;
    std::vector<CpfDefinition*> *vectorCpfDefinition;
    PvarExpression *pVarExpression;
    LogicalExpression *logicalExpression;
    std::vector<LogicalExpression*> *vectorLogicalExpression;
    LConstCaseList *lConstCaseList;
    CaseDefine *caseDefine;
    std::vector<CaseDefine*> *vectorCaseList;
    Parameter *parameter;
    ParameterList *parameterList;

    ObjectDefine *objectDefine;
    std::vector<ObjectDefine*> *objectsList;
    PvariablesInstanceDefine* pvariablesInstanceDefine;
    std::vector<PvariablesInstanceDefine*> *pvariablesInstanceList;

    Instance *instance;
    NonFluentBlock *nonFluentBlock;
}

这是我看到大多数人在解析器中实现多种标记类型的方式。在网上搜索这个答案时,我看到的只是示例,没有解释为什么我们必须使用指针。我现在的任务之一是尽可能 'clean pointers'。所以我的问题是,为什么在这种情况下我们(必须)在联合中使用指针?

编辑:添加了联合中定义的完整类型列表。

您不必使用指针。如您所见,doubleint 都不是指针。

至于"why do we use"部分,我们应该记住union的一些属性。

  1. sizeof union_t 必须至少与最大成员一样大。因此,您不希望按值与单个单词 int 和一些 1KB class 合并。而且指针几乎总是有固定的小尺寸。

  2. 在 C++ 世界中,许多 classes(例如,std::stringstd::vector)具有非平凡的复制构造函数和析构函数。

对于这样的 classes,将它们合并是不安全的。 C++11为此提供了一个"solution",称为unrestricted unions。但即便如此,它也不会按原样工作:对于 union_t object 的每次赋值和销毁,你必须明确地 destruct/construct 一个活跃的联合成员。

非平凡的对象不能存储在联合中,所以只要使用%union,就必须使用指针。但是,Bison 3 提供了一种基于变体的替代方案,它使您不必使用指针。

所以不用

%union
{
  int ival;
  std::string* sval;
}
%token <ival> NUMBER;
%token <sval> STRING;

你会写

%define api.value.type variant
%token <int> NUMBER;
%token <std::string> STRING;

有关详细信息,请参阅 Bison 文档中的 A Complete C++ Example